根据官方文档到的说明
public interface ServletContainerInitializer
Interface which allows a library/runtime to be notified of a web application's startup phase and perform any required programmatic
registration of servlets, filters, and listeners in response to it.
Implementations of this interface may be annotated with HandlesTypes, in order to receive (at their onStartup(java.util.Set>,
javax.servlet.ServletContext) method) the Set of application classes that implement, extend, or have been annotated with the class types specified by the annotation.
If an implementation of this interface does not use this annotation, or none of the application classes match the ones specified
by the annotation, the container must pass a null Set of classes to onStartup(java.util.Set>, javax.servlet.ServletContext).
When examining the classes of an application to see if they match any of the criteria specified by the HandlesTypes annotation
of a ServletContainerInitializer, the container may run into classloading problems if any of the application's optional JAR files
are missing. Because the container is not in a position to decide whether these types of classloading failures will prevent the
application from working correctly, it must ignore them, while at the same time providing a configuration option that would log them.
Implementations of this interface must be declared by a JAR file resource located inside the META-INF/services directory
and named for the fully qualified class name of this interface, and will be discovered using the runtime's service provider lookup
mechanism or a container specific mechanism that is semantically equivalent to it. In either case, ServletContainerInitializer
services from web fragment JAR files excluded from an absolute ordering must be ignored, and the order in which these services
are discovered must follow the application's classloading delegation model.
在容器启动的时候,他会去加载META-INF/services/javax.servlet.ServletContainerInitializer文件下指定的实现类
demo:
创建文件
里面的内容是实现了javax.servlet.ServletContainerInitializer的自定义类,并重写它的方法
@HandlesTypes(value = {HelloService.class}) public class MyServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException { for(Class clazz : set){ System.out.println("获取的class..."+clazz); } } }
@HandlesTypes 可以获取到指定类型的所有子
Set<Class<?>> set获取到的class
servletContext servlet上下文
创建HelloService一个子类型:
启动程序,看打印结果:
获取的class...class com.servlet.HelloServiceImp 获取的class...class com.servlet.AbstractHelloService
在加载的时候,onStartup()方法执行时,我们也可以加入自定义的servlet,filter,listener
servletContext.addServlet("userServlet",UserServlet.class).addMapping("/user"); servletContext.addListener(Mylistener.class); servletContext.addFilter("userFilter",userFilter.class).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/*");
在springmvc中,web容器启动到的时候也会去加载META-INF/services/javax.servlet.ServletContainerInitializer,加载指定的类
SpringServletContainerInitializer
来看看SpringServletContainerInitializer这个类
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>(); if (webAppInitializerClasses != null) { for (Class<?> waiClass : webAppInitializerClasses) { // Be defensive: Some servlet containers provide us with invalid classes, // no matter what @HandlesTypes says... if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { initializers.add((WebApplicationInitializer) waiClass.newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }
@HandlesTypes(WebApplicationInitializer.class)这和servlet中的操作一样,他会去获取到WebApplicationInitializer的子类型
WebApplicationInitializer下三个抽象类:
AbstractContextLoaderInitializer:创建根容器createRootApplicationContext()
@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"); } }
AbstractDispatcherServletInitializer:
1、创建web的ioc容器createServletApplicationContext()
2、创建DispatcherServlet:createDispatcherServlet(servletAppContext)
添加到ServletContext中:servletContext.addServlet(servletName, dispatcherServlet)
@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 + "]"); FrameworkServlet 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); }
AbstractAnnotationConfigDispatcherServletInitializer:
创建根容器:createRootApplicationContext(),它覆写了上面的方法,获取RootConfigClasses类的配置
创建web的ioc容器:createServletApplicationContext() ,它覆写了上面的方法,获取ServletConfigClasses类的配置
@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; }
写一个类MyWebAppInitializer来继承AbstractAnnotationConfigDispatcherServletInitializer
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { RootConfig.class }; } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { App1Config.class }; } @Override protected String[] getServletMappings() { return new String[] { "/app1/*" }; } }
写配置类RootConfig:它不去扫描controller,controller交给App1Config扫描
@ComponentScan(value = "com.springmvc",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})}) public class RootConfig { }
写配置类App1Config:
@ComponentScan(value = "com.springmvc",includeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,classes = {Controller.class})}) public class App1Config { }
这是使用注解来实现配置,用web.xml文件来配置:
<web-app> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/root-context.xml</param-value> </context-param> <servlet> <servlet-name>app1</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/app1-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>app1</servlet-name> <url-pattern>/app1/*</url-pattern> </servlet-mapping> </web-app>