Spring Boot 是如何整合 Spring MVC 的?

? 幫助萬千Java學(xué)習(xí)者持續(xù)成長(zhǎng)

作者|koala
blog.csdn.net/believer123/java/article/details/70196572
B 站搜索:楠哥教你學(xué)Java
獲取更多優(yōu)質(zhì)視頻教程
通過 Spring Boot 整合各個(gè)框架是越來越方便了,整合 Spring MVC 只需要添加對(duì)應(yīng)的 starer 依賴即可。
<dependency>
????<groupId>org.springframework.bootgroupId>
????<artifactId>spring-boot-starter-webartifactId>
dependency>而且還配備了 Tomcat 的 starter。
<dependency>
????<groupId>org.springframework.bootgroupId>
????<artifactId>spring-boot-starter-webartifactId>
dependency>這樣,只需要根據(jù)自身需求,設(shè)置配置文件。啟動(dòng)web服務(wù)器只需要運(yùn)行 java application 就可以了,不再需要部署到 Tomcat 服務(wù)了。
之前一直很好奇,使用 Spring MVC 時(shí)需要在 web.xml 上配置DispatcherServlet。而整合了 Spring Boot 后為什么就不需要配置了,下面就進(jìn)行完整的分析。
看著累?可以直接看步驟 7,核心分析。
1、尋找入口,找到 WebServlet 自動(dòng)配置類:
EmbeddedServletContainerAutoConfiguration。
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration{
????...省略代碼
}Spring Boot 自動(dòng)配置功能類都以 AutoConfiguration 結(jié)尾。
2、注入需要的 Bean
從類上的注解可以看出,導(dǎo)入了 BeanPostProcessorsRegistrar,來添加EmbeddedServletContainerCustomizerBeanPostProcessor。首先會(huì)查看工程是否有自定的。EmbeddedServletContainerCustomizerBeanPostProcessor,如果沒有,則注入默認(rèn)的。EmbeddedServletContainerCustomizerBeanPostProcessor。代碼如下:
@Override
public?void?registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry)?{
????????????if?(this.beanFactory ==?null) {
????????????????return;
????????????}
????????????if?(ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(
????EmbeddedServletContainerCustomizerBeanPostProcessor.class,?true,
????????????????????false))) {
????????????????registry.registerBeanDefinition(?"embeddedServletContainerCustomizerBeanPostProcessor",
????????????????????????new?RootBeanDefinition(
????????EmbeddedServletContainerCustomizerBeanPostProcessor.class));
????????????}
????????????if?(ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(
????????????????????ErrorPageRegistrarBeanPostProcessor.class,?true,?false))) {
????????????????registry.registerBeanDefinition("errorPageRegistrarBeanPostProcessor",
????????????????????????new?RootBeanDefinition(
????????????????????????????????ErrorPageRegistrarBeanPostProcessor.class));
????????????}
????????}實(shí)現(xiàn) ImportBeanDefinitionRegistrar 接口,實(shí)現(xiàn)注入需要的 Bean 到Spring 容器中,Mybatis(MapperScannerRegistrar)也是通過此接口來完成 Mapper 類的定義。
3、步驟 2 注入了 bean:
EmbeddedServletContainerCustomizerBeanPostProcessor,該類實(shí)現(xiàn)了在 ConfigurableEmbeddedServletContainer 對(duì)象初始化前,進(jìn)行行必要的參數(shù)配置。
1、獲取所有EmbeddedServletContainerCustomizer對(duì)象
2、調(diào)用EmbeddedServletContainerCustomizer.customize方法
3、EmbeddedServletContainerCustomizer實(shí)現(xiàn)類根據(jù)自身需求設(shè)置WebServlet容器參數(shù)(如:端口號(hào)、連接數(shù)等等)
核心代碼如下:
@Override
????public?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)throws BeansException {
????????if?(bean?instanceof?ConfigurableEmbeddedServletContainer) { postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
????????}
????????return?bean;
????}
private?void?postProcessBeforeInitialization(ConfigurableEmbeddedServletContainer bean) {
????????for?(EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
????????????customizer.customize(bean);
????????}
????}ConfigurableEmbeddedServletContainer:是 Web 容器的接口,默認(rèn)注入的有。
? ??
BeanPostProcessor :是 Spring 容器的回調(diào)接口,在所有 Bean 初始化之前和之后分別回調(diào)此接口的 postProcessBeforeInitialization,postProcessAfterInitialization 方法。這樣就可以根據(jù)需求在 Bean 初始化前后配置設(shè)置需要的功能。
通過步驟 1-3 完成了 Web 容器啟動(dòng)前的參數(shù)配置功能。
4、EmbeddedWebApplicationContext 入場(chǎng) Spring 容器配置加載完成后,會(huì)回調(diào) EmbeddedWebApplicationContext.refresh 方法。
EmbeddedWebApplicationContext 在執(zhí)行 refresh 方法中,調(diào)用了onRefresh 方法進(jìn)行 ServletContainer 配置。代碼如下:
@Override
????public final void refresh() throws BeansException, IllegalStateException {
?????????????...省略
????????????super.refresh();
?????????????...省略
????}
????@Override
????protected void onRefresh() {
????????...省略
????????//創(chuàng)建ServletContainer
????????createEmbeddedServletContainer();
?????????...省略
????}EmbeddedWebApplicationContext 實(shí)現(xiàn)了接口ConfigurableApplicationContext, Spring 容器配置加載完成后會(huì)回調(diào)所有的 ConfigurableApplicationContext 對(duì)象的 refresh 方法。
(1)、在 onRefresh 方法中,獲取 EmbeddedServletContainerFactory 對(duì)象,因?yàn)楣こ躺鲜褂肨omcat,所以這里就是TomcatEmbeddedServletContainerFactory
(2)、執(zhí)行EmbeddedServletContainerFactory.getEmbeddedServletContainer方法
5、TomcatEmbeddedServletContainerFactory.getEmbeddedServletContainer 這里是 Tomcat 容器核心功能完的地方。主要完成了對(duì) Tomcat 配置(不是這篇重點(diǎn),省略代碼),在 configureContext 方法添加 Tomcat 容器啟動(dòng)回調(diào)接口(重點(diǎn))。
protected?void?configureContext(Context context,
????????????ServletContextInitializer[] initializers)?{
????????TomcatStarter starter =?new?TomcatStarter(initializers);
????????if?(context?instanceof?TomcatEmbeddedContext) {
????????????// Should be true
????????????((TomcatEmbeddedContext) context).setStarter(starter);
????????}
????????...省略
????}ServletContainerInitializer 是 Tomcat 容器啟動(dòng)的一個(gè)回調(diào)接口。
在 Tomcat 啟動(dòng)前,Spring Boot 通過 Tomcat Starte r完成 Servlet,F(xiàn)ilter等 Web 組件的組注入。
6、TomcatStarter,在 Tomcat 啟動(dòng)后回調(diào) onStartup。
7、EmbeddedWebApplicationContext 在 onStartup 回調(diào)中完成 Spring MVC 功能注入。
(1)、在 selfInitialize 方法中獲取到所有 ServletContextInitializer 對(duì)象,并調(diào)用其 onStartup 方法。
(2)、ServletContextInitializer 實(shí)現(xiàn)類如下:
(3)、通過上圖就很清楚的說明了 Servlet,F(xiàn)ilter 等 Web 組件實(shí)現(xiàn)類。
(4)、在 ServletRegistrationBean 向 ServletContainer 添加 Servlet。
@Override
????public?void?onStartup(ServletContext servletContext)?throws?ServletException?{
????????Assert.notNull(this.servlet,?"Servlet must not be null");
????????String name = getServletName();
????????if?(!isEnabled()) {
????????????logger.info("Servlet "?+ name +?" was not registered (disabled)");
????????????return;
????????}
????????logger.info("Mapping servlet: '"?+ name +?"' to "?+?this.urlMappings);
????????Dynamic added = servletContext.addServlet(name,?this.servlet);
????????if?(added ==?null) {
????????????logger.info("Servlet "?+ name +?" was not registered "
????????????????????+?"(possibly already registered?)");
????????????return;
????????}
????????configure(added);
????}(5)、這里也就解釋了 Spring Boot 官方文檔 70.1 節(jié)上為什么是通過RegistrationBean 添加 Servlet 與 Filter 的原因了。
7.6、DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration 此處添加 Spring MVC 核心功能類 DispatcherServlet。
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
????????@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
????????public?ServletRegistrationBean?dispatcherServletRegistration(
????????????????DispatcherServlet dispatcherServlet)?{
????????????ServletRegistrationBean registration =?new?ServletRegistrationBean(
????????????????????dispatcherServlet,?this.serverProperties.getServletMapping());
????????????registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
????????????registration.setLoadOnStartup(
????????????????????this.webMvcProperties.getServlet().getLoadOnStartup());
????????????if?(this.multipartConfig !=?null) {
????????????????registration.setMultipartConfig(this.multipartConfig);
????????????}
????????????return?registration;
????????}以上只是將重要點(diǎn)抽出來說明,貼上全部源碼也是無意義的。要理解其中過程還需要自行查看源碼。
通過以上步驟分析了 Spring Boot 集成 Spring MVC 和 Tomcat 功能簡(jiǎn)要步驟。其實(shí)只要找到了入口,即可 Debug 一步一步的走下去,來查看內(nèi)部實(shí)現(xiàn)。
總結(jié)
通過以上分析和 Mybatis 功能分析,發(fā)現(xiàn)滿滿的都是套路。在 Spring Boot 上實(shí)現(xiàn)自定義 Starter 功能應(yīng)該都是如下套路:
1、在自定義的 XXAutoConfiguration 上 Import 一個(gè)ImportBeanDefinitionRegistrar 來注入指定 Bean。
2、添加自定義的 BeanPostProcessor 在 Bean 初始化之前或之后完成配置功能或初始化某些依賴功能。
1、Spring Boot+Vue項(xiàng)目實(shí)戰(zhàn)
楠哥簡(jiǎn)介
資深 Java 工程師,微信號(hào)?southwindss
《Java零基礎(chǔ)實(shí)戰(zhàn)》一書作者
騰訊課程官方 Java 面試官,今日頭條認(rèn)證大V
GitChat認(rèn)證作者,B站認(rèn)證UP主(楠哥教你學(xué)Java)
致力于幫助萬千 Java 學(xué)習(xí)者持續(xù)成長(zhǎng)。

