通过 WebApplicationInitializer 初始化 Servlet 及 Filter

2016-11-25 18:42:22   最后更新: 2016-11-30 13:57:45   访问数量:1054




上一篇日志中,我们通过继承 AbstractAnnotationConfigDispatcherServletInitializer 实现了 DispatcherServlet 的基本配置,他会创建 DispatcherServlet 和 ContextLoaderListener

DispatcherServlet 简介及配置

但是,如果你需要注册其他的 Servlet、Filter 或 Listener 的话,只是继承 AbstractAnnotationConfigDispatcherServletInitializer 就不够了

如果我们想往 Web 容器中注册其他组件,只需要创建一个新的初始化器就可以了,最简单的方式是实现 Spring 的 WebApplicationInitializer 接口

 

如果我们仍然需要使用 DispatcherServlet,但需要为他增加自定义的 Filter,那么通过复写 AbstractAnnotationConfigDispatcherServletInitializer 的 getServletFilters 方法即可:

@Override protected Filter[] getServletFilters() { return new Filter[] { new TechlogFilter() }; }

 

 

WebApplicationInitializer 接口只有一个方法 onStartup,通过他的参数 servletContext 我们可以实现注册我们的自定义 Servlet 和自定义 Filter 到 SpringMVC 中

 

注册 Servlet

package com.techlog.test.configuration; import org.springframework.web.WebApplicationInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; /** * DispatcherServlet 初始化配置 * Created by techlog on 2016/11/17. */ public class DispatcherServletInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { ServletRegistration.Dynamic techlogServlet = servletContext.addServlet("techlogServlet", TechlogServlet.class); techlogServlet.addMapping("/*"); } }

 

 

注册 Filter

package com.techlog.test.configuration; import org.springframework.web.WebApplicationInitializer; import javax.servlet.FilterRegistration; import javax.servlet.ServletContext; import javax.servlet.ServletException; /** * DispatcherServlet 初始化配置 * Created by techlog on 2016/11/17. */ public class DispatcherServletInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { FilterRegistration.Dynamic filter = servletContext.addFilter("techlogFilter", TechlogFilter.class); filter.addMappingForUrlPatterns(null, false, "/*"); } }

 

 

通过 AbstractAnnotationConfigDispatcherServletInitializer 类,我们可以更加深入的理解 WebApplicationInitializer 和 DispatcherServlet 的初始化过程

如下图所示,AbstractAnnotationConfigDispatcherServletInitializer 类最终也是通过实现 WebApplicationInitializer 实现的

 

 

AbstractContextLoaderInitializer

我们先来看最上层的 AbstractContextLoaderInitializer,他直接实现了 WebApplicationInitializer 接口

package org.springframework.web.context; import javax.servlet.ServletContext; import javax.servlet.ServletException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContextInitializer; import org.springframework.web.WebApplicationInitializer; public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer { /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); @Override public void onStartup(ServletContext servletContext) throws ServletException { registerContextLoaderListener(servletContext); } protected void registerContextLoaderListener(ServletContext servletContext) { WebApplicationContext rootAppContext = createRootApplicationContext(); if (rootAppContext != null) { ContextLoaderListener listener = new ContextLoaderListener(rootAppContext); listener.setContextInitializers(getRootApplicationContextInitializers()); servletContext.addListener(listener); } else { logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not return an application context"); } } protected abstract WebApplicationContext createRootApplicationContext(); protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() { return null; } }

 

 

看上去很简单,他复写了 onStartup 方法,通过 createRootApplicationContext 方法获取了 WebApplication 执行的上下文,然后向 Servlet 上下文注册了一个 Listener

那么 createRootApplicationContext 又是什么呢?我们再往下看

 

AbstractDispatcherServletInitializer

AbstractDispatcherServletInitializer 继承了上面说的 AbstractContextLoaderInitializer

package org.springframework.web.servlet.support; import java.util.EnumSet; import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterRegistration; import javax.servlet.FilterRegistration.Dynamic; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; import org.springframework.context.ApplicationContextInitializer; import org.springframework.core.Conventions; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.web.context.AbstractContextLoaderInitializer; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer { public static final String DEFAULT_SERVLET_NAME = "dispatcher"; @Override public void onStartup(ServletContext servletContext) throws ServletException { super.onStartup(servletContext); registerDispatcherServlet(servletContext); } protected void registerDispatcherServlet(ServletContext servletContext) { String servletName = getServletName(); Assert.hasLength(servletName, "getServletName() must not return empty or null"); WebApplicationContext servletAppContext = createServletApplicationContext(); Assert.notNull(servletAppContext, "createServletApplicationContext() did not return an application " + "context for servlet [" + servletName + "]"); DispatcherServlet dispatcherServlet = createDispatcherServlet(servletAppContext); dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); Assert.notNull(registration, "Failed to register servlet with name '" + servletName + "'." + "Check if there is another servlet registered under the same name."); registration.setLoadOnStartup(1); registration.addMapping(getServletMappings()); registration.setAsyncSupported(isAsyncSupported()); Filter[] filters = getServletFilters(); if (!ObjectUtils.isEmpty(filters)) { for (Filter filter : filters) { registerServletFilter(servletContext, filter); } } customizeRegistration(registration); } protected String getServletName() { return DEFAULT_SERVLET_NAME; } protected abstract WebApplicationContext createServletApplicationContext(); protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext) { return new DispatcherServlet(servletAppContext); } protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() { return null; } protected abstract String[] getServletMappings(); protected Filter[] getServletFilters() { return null; } protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) { String filterName = Conventions.getVariableName(filter); Dynamic registration = servletContext.addFilter(filterName, filter); if (registration == null) { int counter = -1; while (counter == -1 || registration == null) { counter++; registration = servletContext.addFilter(filterName + "#" + counter, filter); Assert.isTrue(counter < 100, "Failed to register filter '" + filter + "'." + "Could the same Filter instance have been registered already?"); } } registration.setAsyncSupported(isAsyncSupported()); registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName()); return registration; } private EnumSet<DispatcherType> getDispatcherTypes() { return (isAsyncSupported() ? EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) : EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE)); } protected boolean isAsyncSupported() { return true; } protected void customizeRegistration(ServletRegistration.Dynamic registration) { } }

 

这个类同样复写了 WebApplicationInitializer 的 onStartup 方法

在 onStartup 方法中,首先执行 super.onStartup 创建 Listener,然后执行 registerDispatcherServlet 注册了 Servlet

registerDispatcherServlet 方法调用 createServletApplicationContext 方法获取了 WebApplication 上下文,然后通过它创建了 DispatcherServlet 对象,从而完成了注册

到这里,不仅上面遗留的问题 -- createRootApplicationContext 方法是什么没有解决,他并没有被实现,而在 AbstractDispatcherServletInitializer 中,又定义了一个新的 abstract 方法 -- createServletApplicationContext,他又是什么呢?

我们再接着往下看

 

AbstractAnnotationConfigDispatcherServletInitializer

AbstractAnnotationConfigDispatcherServletInitializer 继承了虚类 AbstractDispatcherServletInitializer

package org.springframework.web.servlet.support; import org.springframework.util.ObjectUtils; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; public abstract class AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer { @Override protected WebApplicationContext createRootApplicationContext() { Class<?>[] configClasses = getRootConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext(); rootAppContext.register(configClasses); return rootAppContext; } else { return null; } } @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext(); Class<?>[] configClasses = getServletConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { servletAppContext.register(configClasses); } return servletAppContext; } protected abstract Class<?>[] getRootConfigClasses(); protected abstract Class<?>[] getServletConfigClasses(); }

 

 

这个类终于实现了上面所说的 createRootApplicationContext 方法和 createServletApplicationContext 方法,分别调用了暴露出去的 getRootConfigClasses 方法和 getServletConfigClasses 方法获取配置类 class 对象

到这里,我们的所有问题终于迎刃而解,那么接下来,我们就可以梳理出完整的 DispatcherServlet 的初始化流程了

 

抽丝剥茧,我们简化上面源码中的所有逻辑,就可以通过下面的代码实现 DispatcherServlet 的配置了

@Override public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext(); rootAppContext.register(RootConfig.class); servletContext.addListener(new ContextLoaderListener(rootAppContext)); AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext(); servletAppContext.register(WebConfig.class); DispatcherServlet dispatcherServlet = new DispatcherServlet(servletAppContext); ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", dispatcherServlet); registration.setLoadOnStartup(1); registration.addMapping("/"); registration.setAsyncSupported(true); }

 

 






技术帖      web      mvc      技术分享      java      filter      spring      springmvc      servlet      initializer     


京ICP备15018585号