使用spring mvc的时候需要注册DispatcherServlet,DispatcherServlet是一个前端控制器,主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。我们配置DispatcherServlet最典型的是使用web.xml文件。如下:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/spring-mvc.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>
但spring3.1引入了WebApplicationInitializer接口,有了它,我们不需要配置web.xml初始化web应用,只需要继承该接口,通过编码实现相应的配置:
public class Initializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(WebAppConfig.class);
servletContext.addListener(new ContextLoaderListener(ctx));
ctx.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
那通过编码方式有什么好处呢?它可以充分利用编译器,在编译期间检查出配置中的错误,另外,这增强了配置的灵活性和可控性,你可以在启动过程中自定义需要的检查验证条件。
Spring mvc是跟着更底层的接口标准servlet走的,servlet3+以后引入ServletContainerInitializer接口,这为去web.xml,基于代码配置提供了一种途径:
public interface ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException;
}
Spring的SpringServletContainerInitializer 类实现了该接口:
//HandlesTypes注解标识SpringServletContainerInitializer 类启动时需要处理的类,此处专门标识了WebApplicationInitializer,正如前面所展示的,我们正是通过实现WebApplicationInitializer接口.来作一些配置工作的。
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
//以下代码对前面我们实现的WebApplicationInitializer作了循环,实例化,调用onStartUp 等 操作。
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;
}
Collections.sort(initializers, new AnnotationAwareOrderComparator());
servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
tomcat7以后采用了servlet3标准,我们启动tomcat7(servlet3容器)时,容器通过JAR Services API 机制,探测到spring-web包下的一个文件META-INF/services/javax.servlet.ServletContainerInitializer,文件内声明实现类,进而调用其onStartup方法,如果以后我们要自己实现ServletContainerInitializer ,也需要在相应jar包的 META-INF/services目录下放置这样一个以接口的全限定名命名的文件。
程序员局限于框架的使用,将始终停留于浅层次的水平,只有深入框架,理解原理,甚至阅读优化其中的源码,才是升级打怪的王道,大家有空可以去看看源码,我们随时讨论。