看到一些Spring的项目,其web.xml
是空的,但是Spring环境依然正确启动了,颇感好奇。原来这是在Servlet3推出后,Spring就跟进的功能。
Servlet3中,提供了新的注解,可以不依赖web.xml
声明Servlet/过滤器/监听器。同时还提供了一个ServletContainerInitializer
接口,这个接口能够让库代码加入到应用的启动环境中来。这个特性可以看看前一篇文章:Servlet3笔记-注解和可拔插特性 。Spring就是利用了这个接口来实现零配置启动。
说明一下,这里的零配置指的是没有web.xml
中的配置,而不是指Spring本身的配置。
Spring零配置如何使用 一个典型的web.xml
配置可能像这样(参考 ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://java.sun.com/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version ="3.0" > <context-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:applicationContext.xml</param-value > </context-param > <servlet > <servlet-name > dispatcher</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:dispatcher-servlet.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > dispatcher</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > <listener > <listener-class > org.springframework.web.context.ContextLoaderListener</listener-class > </listener > </web-app >
如果使用Spring零配置特性,以上的xml就变成如下的Java代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MyWebAppInitializer implements WebApplicationInitializer { @Override public void onStartup (ServletContext servletContext) throws ServletException { servletContext.setInitParameter("contextConfigLocation" , "classpath:applicationContext.xml" ); ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher" , new DispatcherServlet()); registration.setLoadOnStartup(1 ); registration.addMapping("/" ); registration.setInitParameter("contextConfigLocation" , "classpath:dispatcher-servlet.xml" ); servletContext.addListener(new ContextLoaderListener()); } }
为什么应用中只要实现了Spring提供的WebApplicationInitializer
接口,应用在启动的时候就会触发onStartup
方法呢?
Spring零配置代码分析 首先可以看到spring-web中定义了javax.servlet.ServletContainerInitializer
这个文件,其中指定了org.springframework.web.SpringServletContainerInitializer
这个实现类:
实现类的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @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) { 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); } } }
SpringServletContainerInitializer
是一个很简单的包装,作为应用启动触发的入口。其中把启动的流程转发到了WebApplicationInitializer
实现类中。
Spring零配置别的使用方法 除了直接实现WebApplicationInitializer
接口外,Spring还提供了一些实现了部分功能的抽象类来方便我们使用,就行HttpServlet
之于Servlet
一样:
org.springframework.web.context.AbstractContextLoaderInitializer
org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
一般比较常见的做法是基础AbstractAnnotationConfigDispatcherServletInitializer
这个类,实现其getRootConfigClasses
和getServletConfigClasses
这两个方法,在这两个方法中指定Spring的配置类。
参考资料