深入了解WebApplicationInitializer是如何消除web.xml和springMVC的配置文件

SpringMVC提供了DispatcherServlet来开发web应用,在这篇配置SpringMVC应用的过程和使用,我们有用到web.xml和springmvc-config.xml,,而然springboot也是用到springMVC,那它怎么没有这些配置文件呢。其实servlet3.0后springMVC提供了WebApplicationInitializer接口替代了Web.xml。而JavaConfig的方式替代了springmvc-config.xml。

ServletContainerInitializer接口
这个接口是在在Javax.servlet下的。官方的解释:为了支持可以不使用web.xml。提供了ServletContainerInitializer,它可以通过SPI机制,当启动web容器的时候,会自动到添加的相应jar包下找到META-INF/services下以ServletContainerInitializer的全路径名称命名的文件,它的内容为ServletContainerInitializer实现类的全路径,将它们实例化。

  1. package javax.servlet;
  2. import java.util.Set;
  3. public interface ServletContainerInitializer {
  4. void onStartup(Set<Class> var1, ServletContext var2) throws ServletException;
  5. }

注意:什么时SPI机制?可以看这篇博客:javaweb的spi 的简单应用

SpringServletContainerInitializer实现类
由于官方提供了接口,所以spring那边写了SpringServletContainerInitializer类实现了ServletContainerInitializer接口,为了就是支持可以不使用web.xml

  1. package org.springframework.web;
  2. import java.lang.reflect.Modifier;
  3. import java.util.Iterator;
  4. import java.util.LinkedList;
  5. import java.util.List;
  6. import java.util.Set;
  7. import javax.servlet.ServletContainerInitializer;
  8. import javax.servlet.ServletContext;
  9. import javax.servlet.ServletException;
  10. import javax.servlet.annotation.HandlesTypes;
  11. import org.springframework.core.annotation.AnnotationAwareOrderComparator;
  12. @HandlesTypes({WebApplicationInitializer.class})
  13. public class SpringServletContainerInitializer implements ServletContainerInitializer {
  14. public SpringServletContainerInitializer() {
  15. }
  16. public void onStartup(Set<Class> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
  17. List<WebApplicationInitializer> initializers = new LinkedList();
  18. Iterator var4;
  19. if(webAppInitializerClasses != null) {
  20. var4 = webAppInitializerClasses.iterator();
  21. while(var4.hasNext()) {
  22. Class waiClass = (Class)var4.next();
  23. if(!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
  24. try {
  25. initializers.add((WebApplicationInitializer)waiClass.newInstance());
  26. } catch (Throwable var7) {
  27. throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
  28. }
  29. }
  30. }
  31. }
  32. if(initializers.isEmpty()) {
  33. servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
  34. } else {
  35. servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
  36. AnnotationAwareOrderComparator.sort(initializers);
  37. var4 = initializers.iterator();
  38. while(var4.hasNext()) {
  39. WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
  40. initializer.onStartup(servletContext);
  41. }
  42. }
  43. }
  44. }

这个先判断webAppInitializerClasses这个Set是否为空。如果不为空的话,找到这个set中不是接口,不是抽象类,并且是
WebApplicationInitializer接口实现类的类,将它们保存到list中。当这个list为空的时候,抛出异常。不为空的话就按照一定的顺序排序,并将它们按照一定的顺序实例化,调用其onStartup方法执行。

WebApplicationInitializer
经过上面的几步,最终到了这个接口,所以我们只要实现在这个接口,就可以不用写web.xml文件了

  1. public class MyWebAppInitializer implements WebApplicationInitializer {
  2. @Override
  3. public void onStartup(ServletContext container) {
  4. //实用xml的方式配置SpringMVC
  5. XmlWebApplicationContext appContext = new XmlWebApplicationContext();
  6. //设置配置文件的位置
  7. appContext.setConfigLocation("/WEB-INF/spring/springmvc-config.xml");
  8. //将DispatcherServlet实例添加到容器
  9. ServletRegistration.Dynamic dispatcher =
  10. container.addServlet("dispatcher", new DispatcherServlet(appContext));
  11. //设置启动顺序
  12. dispatcher.setLoadOnStartup(1);
  13. //设置映射路径
  14. dispatcher.addMapping("/");
  15. }
  16. }

这里去除了web.xml文件,但是SpringMVC的配置文件还在,因为我们用XmlWebApplicationContext来加载配置,但是spring提供了AnnotationConfigWebApplicationContext 注解的方式加载配置文件,所以可以这样:

  1. public class MyWebAppInitializer implements WebApplicationInitializer {
  2. @Override
  3. public void onStartup(ServletContext container) {
  4. // 创建Spring的root配置环境
  5. AnnotationConfigWebApplicationContext rootContext =
  6. new AnnotationConfigWebApplicationContext();
  7. rootContext.register(MyMvcConfig.class);
  8. // 将Spring的配置添加为listener
  9. container.addListener(new ContextLoaderListener(rootContext));
  10. // 创建SpringMVC的分发器
  11. AnnotationConfigWebApplicationContext dispatcherContext =
  12. new AnnotationConfigWebApplicationContext();
  13. dispatcherContext.register(DispatcherConfig.class);
  14. // 注册请求分发器
  15. ServletRegistration.Dynamic dispatcher =
  16. container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
  17. dispatcher.setLoadOnStartup(1);
  18. dispatcher.addMapping("/");
  19. }
  20. }

在这里的MyMvcConfig.class是一个JavaConfig的方式:

  1. @Configuration
  2. @EnableWebMvc
  3. @ComponentScan
  4. public class MyMvcConfig{
  5. @Bean
  6. public InternalResourceViewResolver viewResolver {
  7. InternalResourceViewResolver viewResolver=new InternalResourceViewResolver();
  8. viewResolver.setPrefix("/WEB-INF/classes/views");
  9. viewResolver.setSuffix(".jsp");
  10. return viewResolver;
  11. }
  12. }

JavaConfig的方式可以看这篇博客:SpringBoot之@Component@Bean@Configuration配置