<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot 是如何整合 Spring MVC 的?

          共 7450字,需瀏覽 15分鐘

           ·

          2020-09-18 19:01

          ??Java大聯(lián)盟

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

          關(guān)注



          作者|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)

          2、B站:4小時(shí)上手MyBatis Plus

          3、一文搞懂前后端分離

          4、快速上手Spring Boot+Vue前后端分離


          楠哥簡(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)。




          有收獲,就點(diǎn)個(gè)在看?
          瀏覽 72
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人在线看mv | 一区一二三视频 | 美女大香蕉久久 | 激情国产在线观看 | 五月丁香性爱 |