本文重点分析 Spring Boot 是如何自动装配 Spring MVC,分为以下两个部分进行介绍:

  1. Spring 注解驱动原理分析:@EnableWebMvc 取代了 配置。
  2. Spring Boot 自动装配:WebMvcAutoConfiguration 如何自动装配 Spring MVC。

    1. Spring 注解驱动

    @EnableWebMvc 会自动注入 DelegatingWebMvcConfiguration,这个配置类继承自 WebMvcConfigurationSupport。
  • DelegatingWebMvcConfiguration:收集容器中自定义的 WebMvcConfigurer 配置类。
  • WebMvcConfigurationSupport:组装最核心的 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter。
  • WebMvcConfigurer:自定义的配置类,如添加 HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler、HttpMessageConverter 等组件。

    1.1 DelegatingWebMvcConfiguration

    同时 DelegatingWebMvcConfiguration 会收集容器中自定义的 WebMvcConfigurer 配置类。
    1. // 收集容器中所有自定义的WebMvcConfigurer配置类
    2. private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    3. @Autowired(required = false)
    4. public void setConfigurers(List<WebMvcConfigurer> configurers) {
    5. this.configurers.addWebMvcConfigurers(configurers);
    6. }

    1.2 WebMvcConfigurationSupport

    我们看一下 WebMvcConfigurationSupport 的属性,是不是很熟悉呢? ```java private PathMatchConfigurer pathMatchConfigurer; // url路径匹配 private ContentNegotiationManager contentNegotiationManager; // 内容协商 private List> messageConverters; // 媒体类型转换

// 参数处理器、结果处理器 private List argumentResolvers; private List returnValueHandlers;

  1. 有了这些基本的组件,WebMvcConfigurationSupport 则会组装最核心的 RequestMappingHandlerMapping RequestMappingHandlerAdapter
  2. ```java
  3. @Bean
  4. public RequestMappingHandlerMapping requestMappingHandlerMapping(
  5. @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
  6. @Qualifier("mvcConversionService") FormattingConversionService conversionService,
  7. @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
  8. ...
  9. }
  10. @Bean
  11. public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
  12. @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
  13. @Qualifier("mvcConversionService") FormattingConversionService conversionService,
  14. @Qualifier("mvcValidator") Validator validator) {
  15. ...
  16. }

1.3 WebMvcConfigurer

我们可以通过 WebMvcConfigurer 添加一些自定义的组件,如参数解析器、结果处理器、媒体类型转换器等。

  1. void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers);
  2. void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers);
  3. void configureMessageConverters(List<HttpMessageConverter<?>> converters);
  4. ...

2. Spring Boot 自动装配

@SpringBootApplication 注解实际上是 @EnableAutoConfiguration,它会开启自动装配功能,也就是装配 spring.factories 中配置项。

  1. # Auto Configure
  2. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  3. org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
  4. ...

Spring Boot 自动装配基本上和 Spring MVC 类似,涉及到以下配置类:

  • WebMvcAutoConfiguration:自动装配的入口,会自动装配其余的配置类。
  • DispatcherServletAutoConfiguration:装配 DispatcherServlet 组件。
  • WebMvcAutoConfigurationAdapter:配置基础组件。实现了 WebMvcConfigurer 接口。根据 WebMvcProperties 和 ResourceProperties 配置进行组装。
  • EnableWebMvcConfiguration:装配注解驱动相关的组件,如 RequestMappingHandlerAdapter 和 RequestMappingHandlerMapping 等。

说明:Spring Boot 装配的核心组件和 Spring MVC 都是一样的,就不具体展开分析了。不过大多数情况下,Spring MVC 通过 web.xml 配置前端控制器 DispatcherServlet,这是基于 Servlet 2.0 规范。而 Spring Boot 自动装配 DispatcherServlet 则是基于 Servlet 3.0 的规范。我们主要看一下 DispatcherServletAutoConfiguration 是如何装配 Servlet。

2.1 Servlet 3.0

IBM 在《Servlet 3.0 新特性详解》文章中,对 Servlet 3.0 的新特性做了详细的介绍。我们主要是关注 ServletContext 的性能增强这一部分。从 Servlet 3.0 开始,ServletContext 提供的新 API,能在运行时动态添加 Servlet 三大组件,再结合 ServletContainerInitializer 生命周期回调,可实现 Servlet 组件的自动装配。
关于,Servlet 自动装配功能总结如下:

  1. ServletContext 支持动态添加 Servlet 三大组件。
  2. ServletContainerInitializer 启动时可以动态添加 Servlet 组件。另外,ServletContainerInitializer 实现类不需要单独配置,Tomcat 容器启动时,使用 BCEL 字节码技术扫描 classpath 下的所有 class 文件,获取 ServletContainerInitializer 的所有实现类,进行生命周期的回调。

    1. public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet);
    2. public FilterRegistration.Dynamic addFilter(String filterName, Filter filter);
    3. public <T extends EventListener> void addListener(T t);

    如果要向 javaee 容器中动态注册一个 Servlet,示例如下:

    1. // 不需要配置,自动回调
    2. public class MyServletContainerInitializer implements ServletContainerInitializer {
    3. @Override
    4. public void onStartup(Set<Class<?>> set, ServletContext ctx)
    5. throws ServletException {
    6. // 1. 处理感兴趣的类
    7. System.out.println(set);
    8. // 2.1. 注册 Servert
    9. ServletRegistration.Dynamic servlet = ctx.addServlet("myServlet", MyServlet.class);
    10. servlet.addMapping("/*");
    11. // 2.2. 注册 Listener
    12. ctx.addListener(MyServletContextListener.class);
    13. // 2.3. 注册 Filter
    14. FilterRegistration.Dynamic filter = ctx.addFilter("myFileter", MyFilter.class);
    15. filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
    16. }
    17. }

    2.2 Spring Boot 整合

    Spring Boot 整合时,只运用了 Serlvet 3.0 运行时添加 Servlet 三大组件的特性,没有使用 ServletContainerInitializer 生命周期回调。因为 Spring Boot 定义了自己的生命周期回调接口 ServletContextInitializer。
    ServletWebServerApplicationContext 容器启动时,会收集 ServletContextInitializer 对象,并回调其 onStartup 生命周期方法。而 DispatcherServletRegistrationBean 实现了 ServletContextInitializer 接口,调用 onStartup 方法时会自动调用 ServletContext#addServlet 将 DispatcherServlet 注册到容器中。 Spring MVC(四)Spring Boot 自动装配 - 图1说明:DispatcherServletAutoConfiguration 会装配前端控制器 DispatcherServlet,并通过 DispatcherServletRegistrationBean 自动注册到 ServletContext 中。 ```java @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) { DispatcherServlet dispatcherServlet = new DispatcherServlet(); … return dispatcherServlet; }

// ServletWebServerApplicationContext启动时,会自动注册到ServletContext中 @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider multipartConfig) { DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup()); multipartConfig.ifAvailable(registration::setMultipartConfig); return registration; }

  1. <a name="X6JxD"></a>
  2. ## 2.3 内嵌式容器
  3. Spring Boot Web 还有一套特性是内嵌式容器,这一部分我们就重点关注内嵌式容器虽如何装配的。
  4. 1. ServletWebServerFactoryAutoConfiguration:自动装配内嵌容器的工厂类 WebServerFactory。
  5. 1. 内嵌容器配置:EmbeddedTomcat、EmbeddedJetty 等内嵌式容器。
  6. 1. ServletWebServerApplicationContext:容器启动时创建 WebServer 并启动。
  7. 1. ① onRefresh 阶段:调用 createWebServer 创建 WebServer,同时调用 ServletContextInitializer#onStartup;
  8. 1. ② finishRefresh 阶段:调用 startWebServer 启动监听。
  9. | 容器 | Maven 依赖 | WebServer 实现类 |
  10. | --- | --- | --- |
  11. | Tomcat | spring-boot-starter-tomcat | TomcatWebServer |
  12. | Jetty | spring-boot-starter-jetty | JettyWebServer |
  13. <a name="SuKsN"></a>
  14. ### 2.3.1 自动装配
  15. ```java
  16. @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
  17. public class DispatcherServletAutoConfiguration {
  18. }
  19. @EnableConfigurationProperties(ServerProperties.class)
  20. @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
  21. ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
  22. ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
  23. ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
  24. public class ServletWebServerFactoryAutoConfiguration {
  25. }

2.3.2 启动容器

ServletWebServerApplicationContext 启动时,调用 WebServerFactory 创建 WebServer 并启动服务,并回调 ServletContextInitializer#onStartup。ServletContextInitializer 最重要的实现是 DispatcherServletRegistrationBean,它会自动注册 DispatcherServlet。 Spring MVC(四)Spring Boot 自动装配 - 图2

2.3.3 如何配置 Tomcat

Spring Tomcat 整合时提供了两种外部配置化的手段:

  1. 基本配置:ServerProperties#Tomcat 属性。
  2. 自定义配置:直接调用 Tomcat API 配置 Connector、Context、ProtocolHandler,非常灵活,但需要对 Tomcat 非常熟悉。 ```java @Bean @ConditionalOnClass(name = “org.apache.catalina.startup.Tomcat”) public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer(serverProperties); }

@Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider connectorCustomizers, ObjectProvider contextCustomizers, ObjectProvider> protocolHandlerCustomizers) { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.getTomcatConnectorCustomizers() .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatContextCustomizers() .addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatProtocolHandlerCustomizers() .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList())); return factory; } ```

3. 总结时刻

  • Spring @EnableWebMvc 主要是装配 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 这两个核心的组件,其实这两个组件 DispatcherServlet 都会默认装配。另外,Spring 还提供了通过 WebMvcConfigurer 来配置基础组件,如 HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler、HttpMessageConverter 等。
  • Spring Boot 自动装配,除了通过 WebMvcAutoConfiguration 装配上述组件外,还需要内嵌 WebServer 容器,此外 DispatcherServlet 是通过 Servlet 3.0 规范动态添加的。

    推荐阅读


每天用心记录一点点。内容也许不重要,但习惯很重要!