Spring Boot 2.121 版本嵌入式Servlet容器自動配置及啟動原理分析WebServerFactoryCustomizerBeanPostProcessor


1、前言簡單介紹

SpringBoot的自動配置就是SpringBoot的精髓所在;對於SpringBoot項目是不需要配置Tomcat、jetty等等Servlet容器,直接啟動application類既可,SpringBoot為什么能做到這么簡捷?原因就是使用了內嵌的Servlet容器,默認是使用Tomcat的,具體原因是什么?為什么啟動application就可以啟動內嵌的Tomcat或者其它Servlet容器?ok,本文就已SpringBoot嵌入式Servlet的啟動原理簡單介紹一下

環境准備:

  • SmartGit
  • IntelliJ IDEA
  • Maven
  • SpringBoot2.2.1

本文先創建一個SpringBoot項目,基於最新的2.2.1版本,閱讀源碼之前先進行一些必要的應用代碼實踐,先學習應用實踐,有助於理解源碼

SpringBoot官網找到嵌入式Servlet容器相關的描述:
SpringBoot源碼學習系列之嵌入式Servlet容器

Under the hood, Spring Boot uses a different type of ApplicationContext for embedded servlet container support. The ServletWebServerApplicationContext is a special type of WebApplicationContext that bootstraps itself by searching for a single ServletWebServerFactory bean. Usually a TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory has been auto-configured.

這是比較重要的信息,簡單翻譯一下,里面提到ServletWebServerApplicationContext這是一種特殊的ApplicationContext 類,也就是啟動時候,用來掃描ServletWebServerFactory類型的類的,ServletWebServerFactory類是一個接口類,具體的實現類是TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory etc.

2、定制servlet容器

然后如何自定義嵌入式Servlet容器的配置?在官方文檔里找到如圖對應的描述:
SpringBoot源碼學習系列之嵌入式Servlet容器

方法1:修改application配置

從官方文檔可以看出支持的配置有如下所示,所以要修改servlet容器配置,直接在application配置文件修改即可:

  • 網絡設置: 監聽端口(server.port)、服務器地址(server.address)等等
  • Session設置: 會話是否持久 (server.servlet.session.persistent),會話超時(server.servlet.session.timeout), 會話數據的位置 (server.servlet.session.store-dir), 會話對應的cookie配置 (server.servlet.session.cookie.*) 等等
  • 錯誤管理: 錯誤頁面位置 (server.error.path)等等
  • SSL設置:具體參考Configure SSL
  • HTTP compression:具體參考Enable HTTP Response Compression

方法2:自定義WebServerFactoryCustomizer定制器類

從文檔里還找到了通過新建自定義的WebServerFactoryCustomizer類來實現屬性配置修改,WebServerFactoryCustomizer也就是一種定制器類:


import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.stereotype.Component; /** * <pre> * 自定義的WebServerFactory定制器類 * </pre> * @author nicky * <pre> * 修改記錄 * 修改后版本: 修改人: 修改日期: 2019年12月01日 修改內容: * </pre> */ @Component public class WebServerFactoryCustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> { @Override public void customize(ConfigurableServletWebServerFactory server) { server.setPort(8081); } }

ok,官方文檔里提供了如上的代碼實例,當然也可以通過@bean注解進行設置,代碼實例如:

@Configuration public class MyServerConfig { /** * 自定義的WebServerFactory定制器類 * @return */ @Bean public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> webServerFactoryCustomizer(){ return new WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>() { @Override public void customize(ConfigurableServletWebServerFactory factory) { factory.setPort(8082); } }; } }

3、變換servlet容器

SpringBoot2.2.1版本支持的內嵌servlet容器有tomcat、jetty(適用於長連接)、undertow(高並發性能不錯,但是默認不支持jsp),不過項目默認使用的是Tomcat的

我們可以找新建的一個SpringBoot項目,要求是集成了spring-boot-starter-web的工程,在pom文件右鍵->Diagrams->Show Dependencies,可以看到對應的jar關系圖:
SpringBoot源碼學習系列之嵌入式Servlet容器

ok,從圖可以看出,SpringBoot默認使用是Servlet容器是Tomcat,然后如果要切換其它嵌入式Servlet容器,要怎么實現?我們可以在圖示選擇spring-boot-starter-tomcat右鍵exclusion,然后引入其它的servlet容器,pom配置如圖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>

4、servlet容器啟動原理

ok,有了前面應用方面的學習之后,就可以簡單跟一下Springboot是怎么對servlet容器進行自動配置的?內嵌的默認Tomcat容器是怎么樣啟動的?

從之前博客的學習,可以知道Springboot的自動配置都是通過一些AutoConfiguration類進行自動配置的,所以同理本博客也找一些對應的類,ServletWebServerFactoryAutoConfiguration 就是嵌入式servlet容器的自動配置類,簡單跟一下其源碼

@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class)//使ServerProperties配置類起效 @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })//@Import是Spring框架的注解,作用是將對應組件加載到容器,這里關鍵的是BeanPostProcessorsRegistrar,一個后置處理類 public class ServletWebServerFactoryAutoConfiguration { @Bean public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) { return new ServletWebServerFactoryCustomizer(serverProperties); } //Tomcat的定制器類,起作用的條件是有Tomcat對應jar有引入項目的情況,默認是引入的,所以會執行Tomcat的servletWeb工廠定制類 @Bean @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( ServerProperties serverProperties) { return new TomcatServletWebServerFactoryCustomizer(serverProperties); } .... //注冊重要的后置處理器類WebServerFactoryCustomizerBeanPostProcessor,在ioc容器啟動的時候會調用后置處理器 public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; //設置ConfigurableListableBeanFactory @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { if (beanFactory instanceof ConfigurableListableBeanFactory) { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } } @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory == null) { return; } registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class); } private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) { if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) { RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); beanDefinition.setSynthetic(true); registry.registerBeanDefinition(name, beanDefinition); } } } }

從自動配置類里,我們並不能很明確地理解自動配置是怎么運行的,只看重關鍵的一些信息點,比如注冊了Tomcat的ServletWebServer工廠的定制器類,方法是tomcatServletWebServerFactoryCustomizer,還有一個后置處理類BeanPostProcessorsRegistrar,后置處理是Spring源碼里是很關鍵的,所以這里可以繼續點一下TomcatServletWebServerFactoryCustomizer,Tomcat的webServer工廠定制器類

也是一個WebServerFactoryCustomizer類型的類,從前面的應用學習,這個類是進行servlet容器的一些定制
SpringBoot源碼學習系列之嵌入式Servlet容器

這個是關鍵的方法,主要是拿ServerProperties配置類里的信息進行特定屬性定制
SpringBoot源碼學習系列之嵌入式Servlet容器

所以,這里就可以知道Tomcat的配置是通過定制器類TomcatServletWebServerFactoryCustomizer進行定制的,其工廠類是TomcatServletWebServerFactory

TomcatServletWebServerFactory工廠類進行Tomcat對象的創建,必要參數的自動配置
SpringBoot源碼學習系列之嵌入式Servlet容器

ok,簡單跟了一下源碼之后,我們知道了TomcatServletWebServerFactoryCustomizer是Tomcat的定制器類,Tomcat對象的創建是通過TomcatServletWebServerFactory類的,然后,有個疑問,這個定制器類是什么時候創建的?為什么一啟動Application類,嵌入式的Tomcat也啟動了?打成jar格式的Springboot項目,只要運行jar命令,不需要啟動任何servlet容器,項目也是正常運行的?然后這個BeanPostProcessorsRegistrar后置處理類有什么作用?ok,帶着這些疑問,我們還是用調試一下源碼

如圖,打斷點調試,看看Tomcat定制器是怎么創建的?
SpringBoot源碼學習系列之嵌入式Servlet容器

定制器類被調用了,其對應的工廠類也會起作用,打個斷點,看看工廠類是怎么調用的?
SpringBoot源碼學習系列之嵌入式Servlet容器

ok,啟動Application類,在idea里調試,如圖,可以跟着調用順序,一點點跟源碼,如圖所示,調用了Springboot的run方法
SpringBoot源碼學習系列之嵌入式Servlet容器

run方法里的刷新上下文方法,refreshContext其實也就是創建ioc容器,初始化ioc容器,並創建容器的每一個組件
SpringBoot源碼學習系列之嵌入式Servlet容器

這里注意到了,調用到了ServletWebServerApplicationContext類的refresh方法,ServletWebServerApplicationContext類前面也介紹到了,這個類是一種特殊的ApplicationContext類,也就是一些ioc的上下文類,作用於WebServer類型的類
SpringBoot源碼學習系列之嵌入式Servlet容器

創建webServer類,先創建ioc容器,調用基類的onRefresh方法,然后再調用createWebServer方法
SpringBoot源碼學習系列之嵌入式Servlet容器

ioc的servletContext組件沒被創建的情況,調用ServletWebServerFactory類獲取WebServer類,有servletContext的情況,直接從ioc容器獲取
SpringBoot源碼學習系列之嵌入式Servlet容器

掃描ioc容器里是否有對應的ServletWebServerFactory類,TomcatServletWebServerFactory是其中一種,通過調試,是有掃描到的,所以從ioc容器里將這個bean對應的信息封裝到ServletWebServerFactory對象
SpringBoot源碼學習系列之嵌入式Servlet容器

接着是ioc容器創建bean的過程,這個一個比較復雜的過程,因為是單例的,所以是調用singleObjects進行存儲
SpringBoot源碼學習系列之嵌入式Servlet容器

bean被創建之后,調用了后置處理器,這個其實就是Spring的源碼里的bean的創建過程,后置處理器是很關鍵的,在bean被創建,還沒進行屬性賦值時候,就調用了后置處理器
SpringBoot源碼學習系列之嵌入式Servlet容器

關鍵點,這里是檢測是否有WebServerFactory工廠類,前面的調試發現已經有Tomcat的WebServer工廠類,所以是會調用后置處理器的
SpringBoot源碼學習系列之嵌入式Servlet容器

調用了WebServerFactoryCustomizerBeanPostProcessor這個后置處理類,該類負責在bean組件初始化之前執行初始化工作。
該類先從IOC容器中獲取所有類型為WebServerFactoryCustomizer(web服務工廠定制器)的組件:

private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
    return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}

然后拿到一個WebServerFactoryCustomizer定制器類,也就是TomcatWebServerFactoryCustomizer,當然還包含其他定制器,然后實例化定制器,如下圖

 

  1. TomcatWebSocketServletWebServerCustomizer來自於:WebSocketServletAutoConfiguration#webSocketServletWebServerCustomizer(),
    實例化TomcatWebSocketServletWebServerCustomizer同時實例化WebSocketServletAutoConfiguration
  2. ServletWebServerFactoryCustomizer來自於:ServletWebServerFactoryAutoConfiguration#servletWebServerFactoryCustomizer(),
    實例化ServletWebServerFactoryCustomizer同時實例化ServerProperties、ServletWebServerFactoryAutoConfiguration
  3. TomcatServletWebServerFactoryCustomizer來自於:ServletWebServerFactoryAutoConfiguration#tomcatServletWebServerFactoryCustomizer,
    實例化TomcatServletWebServerFactoryCustomizer
  4. TomcatWebServerFactoryCustomizer來自於:TomcatWebServerFactoryCustomizerConfiguration#tomcatWebServerFactoryCustomizer(),
    實例化TomcatWebServerFactoryCustomizer同時實例化TomcatWebServerFactoryCustomizerConfiguration
  5. HttpEncodingAutoConfiguration$LocaleCharsetMappingsCustomizer來自於:HttpEncodingAutoConfiguration#localeCharsetMappings(),
    實例化LocaleCharsetMappingsCustomizer同時實例化了HttpProperties、HttpEncodingAutoConfiguration

通過后置處理器調用定制方法customize方法:

SpringBoot源碼學習系列之嵌入式Servlet容器

然后進行初始化,比如TomcatServletWebServerFactory添加ContextCustomer(Lamda表達式),設置Server和Context的核心參數等等

TomcatWebSocketServletWebServerCustomizer

public void customize(TomcatServletWebServerFactory factory) {
         //給TomcatServletWebServerFactory添加ContextCustomizer
        factory.addContextCustomizers((context) -> context.addApplicationListener(WsContextListener.class.getName()));
    }

在獲取TomcatWebServer的時候會使用添加的ContextCustomizer,代碼如下

TomcatServletWebServerFactory#configureContext(

 /**
     * 使用TomcatServletWebServerFactory准備好的參數配置TomcatEmbeddedContext
     */

    protected void configureContext(Context context, ServletContextInitializer[] initializers) {
        TomcatStarter starter = new TomcatStarter(initializers);
        if (context instanceof TomcatEmbeddedContext) {
            TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
            embeddedContext.setStarter(starter);
            embeddedContext.setFailCtxIfServletStartFails(true);
        }
        /**
         * 將當前ApplicationContext的initializer與TomcatEmbeddedContext綁定
         */
        context.addServletContainerInitializer(starter, NO_CLASSES);
        for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
            context.addLifecycleListener(lifecycleListener);
        }
        for (Valve valve : this.contextValves) {
            context.getPipeline().addValve(valve);
        }
        for (ErrorPage errorPage : getErrorPages()) {
            new TomcatErrorPage(errorPage).addToContext(context);
        }
        for (MimeMappings.Mapping mapping : getMimeMappings()) {
            context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
        }
        configureSession(context);
        new DisableReferenceClearingContextCustomizer().customize(context);
        /**
         * 使用在實例化TomcatServletWebServerFactory時添加的ContextCustomizer改變TomcatEmbeddedContext狀態
         */
        for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
            customizer.customize(context);
        }
    }

ServletWebServerFactoryCustomizer初始化:

public void customize(ConfigurableServletWebServerFactory factory) {
        PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
        //設置端口:8080
        map.from(this.serverProperties::getPort).to(factory::setPort);
        //設置地址,比如:localhost
        map.from(this.serverProperties::getAddress).to(factory::setAddress);
        //設置ContextPath,比如:www.xxx.com/ContextPath/
        map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);
        //設置應用名稱
        map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);
        //設置Session
        map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
        //設置SSL
        map.from(this.serverProperties::getSsl).to(factory::setSsl);
        //設置JSP,用以支持JSP SERVLET
        map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
        map.from(this.serverProperties::getCompression).to(factory::setCompression);
        //設置HTTP2,用以支持HTTP/2
        map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
        //設置Header
        map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
        //設置初始化參數
        map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);
    }

TomcatServletWebServerFactoryCustomizer初始化:

public void customize(TomcatServletWebServerFactory factory) {
        ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
        if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
            factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns());
        }
        //是否支持使用"/"重定向到根路徑
        if (tomcatProperties.getRedirectContextRoot() != null) {
            customizeRedirectContextRoot(factory, tomcatProperties.getRedirectContextRoot());
        }
        //是否支持相對或者絕對路徑的重定向
        if (tomcatProperties.getUseRelativeRedirects() != null) {
            customizeUseRelativeRedirects(factory, tomcatProperties.getUseRelativeRedirects());
        }
    }

TomcatWebServerFactoryCustomizer定制器調用customize()定制方法,獲取到Servlet容器相關配置類ServerProperties,設置容器的相關核心參數,比如Basedir,最大最小線程數等等進行自動配置:

public void customize(ConfigurableTomcatWebServerFactory factory) {
    ServerProperties properties = this.serverProperties;
    Tomcat tomcatProperties = properties.getTomcat();
    PropertyMapper propertyMapper = PropertyMapper.get();
    tomcatProperties.getClass();
    propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);
    tomcatProperties.getClass();
    propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds).as(Long::intValue).to(factory::setBackgroundProcessorDelay);
    this.customizeRemoteIpValve(factory);
    tomcatProperties.getClass();
    propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive).to((maxThreads) -> {
        this.customizeMaxThreads(factory, tomcatProperties.getMaxThreads());
    });
    tomcatProperties.getClass();
    propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive).to((minSpareThreads) -> {
        this.customizeMinThreads(factory, minSpareThreads);
    });
    propertyMapper.from(() -> {
        return this.determineMaxHttpHeaderSize();
    }).when(this::isPositive).to((maxHttpHeaderSize) -> {
        this.customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize);
    });
    tomcatProperties.getClass();
    propertyMapper.from(tomcatProperties::getMaxHttpPostSize).when((maxHttpPostSize) -> {
        return maxHttpPostSize != 0;
    }).to((maxHttpPostSize) -> {
        this.customizeMaxHttpPostSize(factory, maxHttpPostSize);
    });
    tomcatProperties.getClass();
    propertyMapper.from(tomcatProperties::getAccesslog).when(Accesslog::isEnabled).to((enabled) -> {
        this.customizeAccessLog(factory);
    });
    tomcatProperties.getClass();
    propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
    properties.getClass();
    propertyMapper.from(properties::getConnectionTimeout).whenNonNull().to((connectionTimeout) -> {
        this.customizeConnectionTimeout(factory, connectionTimeout);
    });
    tomcatProperties.getClass();
    propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive).to((maxConnections) -> {
        this.customizeMaxConnections(factory, maxConnections);
    });
    tomcatProperties.getClass();
    propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive).to((acceptCount) -> {
        this.customizeAcceptCount(factory, acceptCount);
    });
    this.customizeStaticResources(factory);
    this.customizeErrorReportValve(properties.getError(), factory);
}

然后WebServerFactoryCustomizerBeanPostProcessor這個后置處理器是什么注冊的?往前翻Springboot的自動配置類,在這里找到了WebServerFactoryCustomizerBeanPostProcessor的注冊

SpringBoot源碼學習系列之嵌入式Servlet容器

ok,繼續調試源碼,BeanWrapperImpl創建bean實例
SpringBoot源碼學習系列之嵌入式Servlet容器

ok,Tomcat定制器類被調用了,是通過后置處理器調用的
SpringBoot源碼學習系列之嵌入式Servlet容器

然后就是之前跟過的定制方法customize執行:
SpringBoot源碼學習系列之嵌入式Servlet容器

Tomcat的WebServer工廠類創建Tomcat對象實例,進行屬性配置,引擎設置等等
SpringBoot源碼學習系列之嵌入式Servlet容器

端口有設置就創建TomcatwebServer對象
SpringBoot源碼學習系列之嵌入式Servlet容器

TomcatWebServer啟動Tomcat,如圖代碼所示:
SpringBoot源碼學習系列之嵌入式Servlet容器

ok,跟了源碼,您是否有一個疑問?Tomcat的工廠類TomcatServletWebServerFactory是什么時候創建的?好的,返回前面配置類看看,如圖,這里用import引入了EmbeddedTomcat類
SpringBoot源碼學習系列之嵌入式Servlet容器

ok,EmbeddedTomcat是一個內部的配置類,條件是有引入Tomcat對應的jar,就會自動創建工廠類,很顯然,Springboot默認是有引入的
SpringBoot源碼學習系列之嵌入式Servlet容器

ok,經過源碼比較簡單的學習,思路就很清晰了

Springboot的ServletWebServerFactoryAutoConfiguration是嵌入式Servlet容器的自動配置類,這個類的主要作用是創建TomcatServletWebServerFactory工廠類,創建定制器類,默認會創建5個定制器,包括TomcatServletWebServerFactoryCustomizer等。如下圖

 

 它還會創建FilterRegistrationBean類,同時很關鍵的一步是注冊后置處理器webServerFactoryCustomizerBeanPostProcessor

然后Springboot的Application類一啟動,就會執行run方法,run經過一系列調用會通過ServletWebServerApplicationContext的onRefresh方法創建ioc容器,然后通過createWebServer方法,createWebServer方法會去ioc容器里掃描是否有對應的ServletWebServerFactory工廠類(TomcatServletWebServerFactory是其中一種),掃描得到,就會觸發webServerFactoryCustomizerBeanPostProcessor后置處理器類,這個處理器類會獲取TomcatServletWebServerFactoryCustomizer定制器,並調用customize方法進行定制,這時候工廠類起作用,調用getWebServer方法進行Tomcat屬性配置和引擎設置等等,再創建TomcatWebServer啟動Tomcat容器

ok,本問只是簡單跟一下嵌入式Tomcat容器的啟動過程,可以看出Springboot的強大,還是基於Spring框架的,比如本博客提到的后置處理器,以及bean工程創建bean實例的過程,都是通過Spring框架實現的。

 

本文轉自:

https://www.jianshu.com/p/017a7f40efff

https://blog.51cto.com/14230003/2456912

https://www.liangzl.com/get-article-detail-160269.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM