<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>

          SpringBoot 整合 SpringMVC 原理探究

          共 7210字,需瀏覽 15分鐘

           ·

          2020-11-18 09:36

          程序員的成長之路
          互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
          關(guān)注


          閱讀本文大概需要 4 分鐘。

          作者:koala
          https://blog.csdn.net/believer123/java/article/details/70196572


          通過SpringBoot整合各個(gè)框架是越來越方便了,整合SpringMVC只需要添加對(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ù)了。

          之前一直很好奇,使用SpringMVC時(shí)需要在web.xml上配置DispatcherServlet。而整合了SpringBoot后為什么就不需要配置了,下面就進(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{
          ????...省略代碼
          }

          SpringBoot 自動(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入場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)前,SpringBoot通過TomcatStarter完成Servlet,F(xiàn)ilter等Web組件的組注入

          6、TomcatStarter,在Tomcat啟動(dòng)后回調(diào)onStartup。

          7、EmbeddedWebApplicationContext在onStartup回調(diào)中完成SpringMvc功能注入

          7.1、在selfInitialize方法中獲取到所有ServletContextInitializer對(duì)象,并調(diào)用其onStartup方法

          7.2、ServletContextInitializer實(shí)現(xiàn)類如下:


          7.3、通過上圖就很清楚的說明了Servlet,F(xiàn)ilter等Web組件實(shí)現(xiàn)類

          7.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);
          ????}

          7.5、這里也就解釋了SpringBoot官方文檔70.1節(jié)上為什么是通過RegistrationBean添加Servlet與Filter的原因了。


          7.6、DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration此處添加SpringMVC核心功能類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)抽出來說明,貼上全部源碼也是無意義的。要理解其中過程還需要自行查看源碼。

          通過以上步驟分析了SpringBoot集成SpringMVC和Tomcat功能簡要步驟。其實(shí)只要找到了入口,即可Debug一步一步的走下去,來查看內(nèi)部實(shí)現(xiàn)。

          總結(jié)

          通過以上分析和Mybatis功能分析,發(fā)現(xiàn)滿滿的都是套路。在SpringBoot上實(shí)現(xiàn)自定義Starter功能應(yīng)該都是如下套路:

          1、在自定義的XXAutoConfiguration上Import一個(gè)ImportBeanDefinitionRegistrar來注入指定Bean

          2、添加自定義的BeanPostProcessor在Bean初始化之前或之后完成配置功能或初始化某些依賴功能

          推薦閱讀:

          是誰投了特朗普?黑客在暗網(wǎng)出售美國1.86億選民數(shù)據(jù),F(xiàn)BI介入

          如何實(shí)現(xiàn) MySQL 刪除重復(fù)記錄并且只保留一條

          5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機(jī),樹莓派,等等。在公眾號(hào)內(nèi)回復(fù)「2048」,即可免費(fèi)獲取!!

          微信掃描二維碼,關(guān)注我的公眾號(hào)

          朕已閱?

          瀏覽 41
          點(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>
                  99久久久久久99久久久久久 | 91POR中文字幕 | 亚洲视频在线视频观看视频在线 | 人成在线免费视频 | 亚洲v日韩V综合V精品V |