行业知识
SpringBoot源码解读与原理分析(六)WebMvc场景的自动装配
文章目录
- 2.6 WebMvc场景下的自动装配原理
- 2.6.1 WebMvcAutoConfiguration
- 2.6.2 Servlet容器的装配
- 2.6.2.1 EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow
- 2.6.2.2 BeanPostProcessorsRegistrar(后置处理器的注册器)
- 2.6.2.3 两个定制器的注册
- 2.6.3 DispatcherServlet的装配
- 2.6.3.1 SpringBoot注册Sevlet原生组件
- 2.6.3.2 DispatcherServlet的注册
- 2.6.4 SpringWebMvc的装配
- 2.6.4.1 WebMvcAutoConfigurationAdapter
- (1)配置消息转换器
- (2)配置异步支持
- (3)注册视图解析器
- (5)配置国际化支持
- (5)配置RequestContextHolder支持
- 2.6.4.2 EnableWebMvcConfiguration
- (1)注册HandlerMapping
- (2)注册HandlerAdapter
- (3)静态资源加载配置
- 2.7 ���结
2.6 WebMvc场景下的自动装配原理
了解了SpringBoot的自动装配机制之后,研究一个常见且实用的场景:当项目整合SpringWebMvc后SpringBoot的自动装配都做了什么?
()2.6.1 WebMvcAutoConfiguration
引入spring-boot-starter-web依赖后,SpringBoot会进行WebMvc的自动装配,处理的核心是一个叫WebMvcAutoConfiguration的自动配置类。
@Configuration(proxyBeanMethods = false)// 当前环境必须是WebMvc(Servlet)环境@ConditionalOnWebApplication(type = Type.SERVLET)// 当前运行环境的classpath必须有Servlet类、DispatcherServlet类、WebMvcConfigurer类@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })// 如果没有自定义的WebMvc配置类,则使用本自动配置@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)// 当前自动配置会在以下几个配置类解析后再处理@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class })public class WebMvcAutoConfiguration由以上源码可知,WebMvcAutoConfiguration自动配置类的生效需要满足以下条件:
()- 当前环境必须是WebMvc(Servlet)环境。引入spring-boot-starter-web依赖后,该条件默认生效。
- 当前类路径下必须有Servlet类、DispatcherServlet类、WebMvcConfigurer类。
- 项目中没有自定义的WebMvcConfigurationSupport类或子类,WebMvcAutoConfiguration才会生效。
- DispatcherServletAutoConfiguration、TaskExecutionAutoConfiguration、ValidationAutoConfiguration会先于WebMvcAutoConfiguration进行解析。
拓展:
DispatcherServlet是前置控制器。拦截匹配的请求,把拦截下来的请求,依据相应的规则分发到目标Controller来处理。
进一步查看源码发现,DispatcherServletAutoConfiguration自动配置类解析之前,ServletWebServerFactoryAutoConfiguration会先解析。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@Configuration(proxyBeanMethods = false)@ConditionalOnWebApplication(type = Type.SERVLET)@ConditionalOnClass(DispatcherServlet.class)// 当前自动配置会在ServletWebServerFactoryAutoConfiguration配置类解析后再处理@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)public class DispatcherServletAutoConfiguration
所以大体上可以梳理出WebMvc场景的自动装配环节:Servlet容器的装配→DispatcherServlet的装配→WebMvc核心组件的装配。
2.6.2 Servlet容器的装配
嵌入式Servlet容器的装配,导入了几个组件,分别是一个BeanPostProcessorsRegistrar和三个Embedded容器内部类。
@Configuration(proxyBeanMethods = false)@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@ConditionalOnClass(ServletRequest.class)@ConditionalOnWebApplication(type = Type.SERVLET)@EnableConfigurationProperties(ServerProperties.class)@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })public class ServletWebServerFactoryAutoConfiguration2.6.2.1 EmbeddedTomcat、EmbeddedJetty、EmbeddedUndertow
这三个容器其实是一码事。默认情况下,SpringBoot会整合嵌入式Tomcat(EmbeddedTomcat)作为可独立运行jar文件的Web容器。如果需要切换,只需要在pom文件中移除Tomcat依赖,在添加新的嵌入式Servlet容器依赖。
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-jetty provided
启动主启动类,会发现嵌入式Servlet容器更换成了Jetty。
2024-01-05 16:04:25.870 INFO 48224 --- [main] o.s.b.web.embedded.jetty.JettyWebServer : Jetty started on port(s) 8080 (http/1.1) with context path '/'2024-01-05 16:04:25.898 INFO 48224 --- [main] c.s.springboot.assemble.test02.JettyApp : Started JettyApp in 3.268 seconds (JVM running for 6.407)
那底层是如何确定该实例化哪个嵌入式Web容器?这是由它们的嵌入式内部类决定的。以EmbeddedTomcat为例:
@Configuration(proxyBeanMethods = false)@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)static class EmbeddedTomcat { @Bean TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider connectorCustomizers, ObjectProvider contextCustomizers, ObjectProvider beanClass) { if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) { RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); beanDefinition.setSynthetic(true); registry.registerBeanDefinition(name, beanDefinition); } }}由以上源码可知,BeanPostProcessorsRegistrar本身是一个ImportBeanDefinitionRegistrar,它注册了两个后置处理器组件:
- WebServerFactoryCustomizerBeanPostProcessor:负责执行所有webServerFactoryCustomizer(嵌入式Web容器的定制器)
- ErrorPageRegistrarBeanPostProcessor:负责向嵌入式Web容器注册默认的错误提示页面。
2.6.2.3 两个定制器的注册
ServletWebServerFactoryAutoConfiguration除了使用@Import导入组件,还注册了两个定制器。
public class ServletWebServerFactoryAutoConfiguration { @Bean public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) { return new ServletWebServerFactoryCustomizer(serverProperties); } @Bean @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer(serverProperties); } // ...}这两个定制器都把ServerProperties作为构造方法的参数传入到定制器中,而ServerProperties定义了有关Web容器的一些配置(如端口、上下文路径、开启SSL等),这些配置对应SpringBoot全局配置文件的server.*部分。
因此,这两个定制器的作用就是将全局配置文件中定义的配置属性实际应用在嵌入式Web容器中,达到“外部配置内部生效”的效果。
2.6.3 DispatcherServlet的装配
2.6.3.1 SpringBoot注册Sevlet原生组件
在解释DispatcherServlet的装配之前,需要了解一下SpringBoot注册Sevlet原生组件的方式。
基于SpringBoot的项目,底层都会采用Servlet 3.0及以上的规范。Servlet 3.0不再使用web.xml,而是使用注解的方式配合Servlet容器扫描完成原生组件的注册。
SpringBoot本身并不默认支持扫描Servlet三大组件,而是提供了两外两种注册方式。
- Servlet原生组件扫描@ServeltComponentScan
这种扫描方式适用于自定义的Servlet原生组件。在SpringBoot主启动类上标注@ServeltComponentScan注解后,则会自动扫描主启动类所在包及其子包下的所有Servlet原生组件,要注意的是这些原生组件必须标注@WebServlet、@WebFilter、@WebListener注解(就像@ComponentScan配合@Component注解一样)。
- 借助辅助注册器RegistrationBean
这种方式适用于引入项目依赖的jar包中存在Servlet原生组件。由于引入的第三方库中的代码不可修改,因此依靠Servlet原生组件扫描的方式是不现实的。为此SpringBoot引入了辅助注册器RegistrationBean来注册Servlet原生组件。
2.6.3.2 DispatcherServlet的注册
DispatcherServlet的注册在DispatcherServletAutoConfiguration中完成,其核心是两个内部类:
- DispatcherServletConfiguration
@Configuration(proxyBeanMethods = false)@Conditional(DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)// 负责注册DispatcherServlet本身protected static class DispatcherServletConfiguration { @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) { DispatcherServlet dispatcherServlet = new DispatcherServlet(); // 设置DispatcherServlet的参数以定制化 dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest()); dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound()); dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents()); dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails()); return dispatcherServlet; } @Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver(MultipartResolver resolver) { // Detect if the user has created a MultipartResolver but named it incorrectly return resolver; }}- DispatcherServletRegistrationConfiguration
// 利用ServletRegistrationBean的子类DispatcherServletRegistrationBean将DispatcherServlet注册到Web容器@Configuration(proxyBeanMethods = false)@Conditional(DispatcherServletAutoConfiguration.DispatcherServletRegistrationCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)@Import(DispatcherServletAutoConfiguration.DispatcherServletConfiguration.class)protected static class DispatcherServletRegistrationConfiguration { @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) { // 将DispatcherServlet注册到Web容器 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; }}经过DispatcherServlet的实例化和注册到Web容器,DispatcherServlet的装配工作完成。
2.6.4 SpringWebMvc的装配
WebMvc的装配在WebMvcAutoConfiguration中完成,核心是其中的两个内部类WebMvcAutoConfigurationAdapter和EnableWebMvcConfiguration。
2.6.4.1 WebMvcAutoConfigurationAdapter
WebMvcAutoConfigurationAdapter实现了WebMvcConfigurer接口,重写了大量方法,并注册了一些新的Bean。因此,WebMvcAutoConfigurationAdapter是一个以WebMvc配置为主的配置器。
(1)配置消息转换器
@Overridepublic void configureMessageConverters(List
- DispatcherServletRegistrationConfiguration
- DispatcherServletConfiguration
- Servlet原生组件扫描@ServeltComponentScan
上一篇:照片怎么存入服务器?
下一篇:web 做题随身记