一、Servlet 3 扩展接口

1.1、ServletContainerInitializer 容器初始化接口

  • 主要用于在容器启动阶段通过编程风格注册Filter、Servlet以及Listener,以取代通过web.xml配置注册

    1. public interface ServletContainerInitializer {
    2. public void onStartup(Set<Class<?>> c, ServletContext ctx)
    3. throws ServletException;
    4. }

    1.2、指定初始化类的注解 HandlesTypes

    ```java @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface HandlesTypes {

    //指定初始化的类 Class<?>[] value(); }

  1. <a name="P3LyO"></a>
  2. ### 1.3、原理
  3. - Tomcat 启动时,通过SPI 机制,找到在路径META-INF/services/javax.servlet.ServletContainerInitializer下定义的初始化类,并实例化
  4. - 通过注解 @HandlesTypes 获取到自定义的初始化类
  5. - 执行调用 ServletContainerInitializer#onStartup
  6. - 执行链路

StandardContext #startInternal() —> StandardContext父类LifecycleBase#fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); —> //LifecycleListener 实现ContextConfig ContextConfig#lifecycleEvent(event)
—> ContextConfig #configureStart(); —> ContextConfig #webConfig(); —> ContextConfig #processServletContainerInitializers —> //加载完,保存到 StandardContext#initializers中 WebappServiceLoader#load(Class serviceType) —> 遍历StandardContext#initializers,执行ServletContainerInitializer

  1. <a name="w7VJY"></a>
  2. ## 二、纯 Java SpringMVC 配置
  3. <a name="icPB1"></a>
  4. ### 2.1、实现WebApplicationInitializer
  5. ```java
  6. public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
  7. /**
  8. * Spring Root上下文配置
  9. *
  10. * @return
  11. */
  12. protected Class<?>[] getRootConfigClasses() {
  13. return new Class[]{RootConfig.class};
  14. }
  15. /**
  16. * SpringMVC 配置
  17. *
  18. * @return
  19. */
  20. protected Class<?>[] getServletConfigClasses() {
  21. return new Class[]{WebMvcConfig.class};
  22. }
  23. /**
  24. * MVC 映射路径
  25. *
  26. * @return
  27. */
  28. protected String[] getServletMappings() {
  29. return new String[]{
  30. "/*"
  31. };
  32. }
  33. }

2.2、Root配置

  1. @Configuration
  2. @ComponentScan(value = "cn.hdj.mvc", excludeFilters = {
  3. //排除扫描 controller
  4. @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
  5. })
  6. public class RootConfig {
  7. }

2.3、MVC 配置

  1. @Configuration
  2. @ComponentScan(value = "cn.hdj.mvc.modules",
  3. includeFilters = {
  4. @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
  5. }, useDefaultFilters = false)
  6. @EnableWebMvc
  7. public class WebMvcConfig extends WebMvcConfigurerAdapter {
  8. /**
  9. * 配置视图解析器
  10. *
  11. * @return
  12. */
  13. @Bean
  14. public ViewResolver viewResolver() {
  15. InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
  16. viewResolver.setPrefix("/WEB-INF/view/");
  17. viewResolver.setSuffix(".jsp");
  18. return viewResolver;
  19. }
  20. /**
  21. * <!-- 配置静态资源用WEB容器默认的servlet来处理 -->
  22. * <mvc:default-servlet-handler/>
  23. * @param configurer
  24. */
  25. @Override
  26. public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
  27. configurer.enable();
  28. }
  29. }

2.4、Spring 的ServletContainerInitializer 实现

2.4.1、在 mvc jar包中定义ServletContainerInitializer

  • 文件路径 ``` META-INF\services\javax.servlet.ServletContainerInitializer

//文件内容 org.springframework.web.SpringServletContainerInitializer

  1. - SpringServletContainerInitializer
  2. ```java
  3. @HandlesTypes(WebApplicationInitializer.class)
  4. public class SpringServletContainerInitializer implements ServletContainerInitializer {
  5. @Override
  6. public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
  7. throws ServletException {
  8. List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
  9. if (webAppInitializerClasses != null) {
  10. for (Class<?> waiClass : webAppInitializerClasses) {
  11. // Be defensive: Some servlet containers provide us with invalid classes,
  12. // no matter what @HandlesTypes says...
  13. if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
  14. WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
  15. try {
  16. //实例化 WebApplicationInitializer
  17. initializers.add((WebApplicationInitializer) waiClass.newInstance());
  18. }
  19. catch (Throwable ex) {
  20. throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
  21. }
  22. }
  23. }
  24. }
  25. if (initializers.isEmpty()) {
  26. servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
  27. return;
  28. }
  29. servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
  30. AnnotationAwareOrderComparator.sort(initializers);
  31. for (WebApplicationInitializer initializer : initializers) {
  32. //调用 WebApplicationInitializer
  33. initializer.onStartup(servletContext);
  34. }
  35. }
  36. }

三、SpringMVC Java Config 启动整体过程

  • Tomcat 启动时,通过SPI 机制,找到在路径META-INF/services/javax.servlet.ServletContainerInitializer下定义的初始化类,并实例化
  • 通过注解 @HandlesTypes 获取到自定义的初始化类
  • 执行调用 ServletContainerInitializer#onStartup
  • 在 SpringServletContainerInitializer#onStartup中创建 ContextLoaderListener,并设置到ServletContext中
  • 执行 ContextLoaderListener 监听器,初始化 Spring 上下文

四、简单内嵌Tomcat启动

4.1、添加依赖

  1. <dependency>
  2. <groupId>javax.servlet</groupId>
  3. <artifactId>javax.servlet-api</artifactId>
  4. <version>3.1.0</version>
  5. </dependency>
  6. <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
  7. <dependency>
  8. <groupId>org.apache.tomcat.embed</groupId>
  9. <artifactId>tomcat-embed-core</artifactId>
  10. <version>8.5.81</version>
  11. </dependency>
  12. <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
  13. <dependency>
  14. <groupId>org.apache.tomcat.embed</groupId>
  15. <artifactId>tomcat-embed-jasper</artifactId>
  16. <version>8.5.81</version>
  17. </dependency>

4.2、启动

  1. public class MvcJavaConfigApplication {
  2. public static void main(String[] args) throws LifecycleException {
  3. Tomcat tomcat = new Tomcat();
  4. Server server = tomcat.getServer();
  5. Service service = tomcat.getService();
  6. service.setName("Tomcat-embbeded");
  7. Connector connector = new Connector("HTTP/1.1");
  8. connector.setPort(8080);
  9. service.addConnector(connector);
  10. //应用目录
  11. tomcat.addWebapp("", System.getProperty("user.dir") + File.separator);
  12. server.start();
  13. System.out.println("tomcat服务器启动成功..");
  14. System.out.println("http://127.0.0.1:8080");
  15. server.await();
  16. }
  17. }

项目Demo