Spring源碼之六-onRefresh()方法

原文地址:https://segmentfault.com/a/1190000041493352

今天帶大家解讀Spirng源碼之六的onRefresh()方法,這是refresh()的其中的一個(gè)方法,看似是一個(gè)空方法,實(shí)則他是非常非常重要的,對(duì)于提高Spring的擴(kuò)展性。

老規(guī)矩,先貼上Spring的核心方法refresh()方法的源碼,以便讀者可以絲滑入戲。

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            //1、刷新前的準(zhǔn)備
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //2、將會(huì)初始化 BeanFactory、加載 Bean、注冊(cè) Bean
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            //3、設(shè)置 BeanFactory 的類加載器,添加幾個(gè) BeanPostProcessor,手動(dòng)注冊(cè)幾個(gè)特殊的 bean
            prepareBeanFactory(beanFactory);

            try {
                //4、模板方法
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                //執(zhí)行BeanFactory后置處理器
                invokeBeanFactoryPostProcessors(beanFactory);

                // 5、Register bean processors that intercept bean creation.
                //注冊(cè)bean后置處理器
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                //國際化
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                //6、模板方法--springboot實(shí)現(xiàn)了這個(gè)方法
                onRefresh();

                // Check for listener beans and register them.
                //7、注冊(cè)監(jiān)聽器
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                //8、完成bean工廠的初始化**方法**********************************************
                finishBeanFactoryInitialization(beanFactory);

                //9、 Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }
onRefresh()是模板方法,具體的子類可以在這里初始化一些特殊的 Bean(在初始化 singleton beans 之前)

這是onRefresh()的主要作用,那么文章到這里就結(jié)束了,感謝閱讀!

開玩笑,只說作用不舉例那和耍流氓沒有什么區(qū)別,接下來就以Spirng的典型實(shí)現(xiàn)Springboot來舉例。

該方法的執(zhí)行時(shí)機(jī)是Spring已經(jīng)加載好了一些特殊的bean(內(nèi)置的一些bean,實(shí)現(xiàn)了bean工廠后置處理器的類)之后,在實(shí)例化單例bean之前。讓我們來看Springboot是怎么調(diào)用這個(gè)模板方法的。

一路的點(diǎn)擊Springboot的核心入口run()方法,一路找到了我們今天的主角,Spring的refresh()方法中的onRefresh()方法。

點(diǎn)擊查看Springboot的onRresh()的實(shí)現(xiàn)方法。

有兩個(gè)包路徑含有boot的,一定就是Spirngboot的實(shí)現(xiàn)方法。

image.png

這是Spirng的onRresh()的實(shí)現(xiàn)方法。

image.png

比對(duì)一下Spirng的onRresh()和SpirngbootRefersh的實(shí)現(xiàn)類對(duì)比,Springboot多了兩個(gè)實(shí)現(xiàn)類,ReactiveWebServerApplicationContext類和ServletWebServerApplicationContext類。

我們分別查看這兩個(gè)實(shí)現(xiàn)的onRresh()方法都做了什么?

方法名都是createWebServer()方法,以為這兩個(gè)方法都是一個(gè)方法,仔細(xì)一看發(fā)現(xiàn)并不是。

image.png

兩個(gè)createWebServer()方法做了什么呢?我們debug進(jìn)去摟一眼。

ReactiveWebServerApplicationContext類的onRresh()方法并沒有執(zhí)行到,見名知意應(yīng)該是跟webServer管理相關(guān)的,限于篇幅問題,留個(gè)坑暫時(shí)放在吧。

image.png

ServletWebServerApplicationContext類的onRefresh()方法執(zhí)行到了,我們進(jìn)去一探究竟。

    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        //第一次進(jìn)來webServer servletContext都是null,會(huì)進(jìn)到if分支里面
        if (webServer == null && servletContext == null) {
            //這里就會(huì)來查找ServletWebServerFactory,也就是web容器的工廠,具體看下getWebServerFactory()方法,
            // 還是ServletWebServerApplicationContext這個(gè)類的方法
            //創(chuàng)建了 TomcatServletWebServerFactory 類
            ServletWebServerFactory factory = getWebServerFactory();
            //創(chuàng)建 Tomcat
            this.webServer = factory.getWebServer(getSelfInitializer());
        }
        else if (servletContext != null) {
            try {
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        initPropertySources();
    }

核心應(yīng)該是 factory.getWebServer(getSelfInitializer()),這個(gè)方法是創(chuàng)建了一個(gè)容器。都有哪些容器呢?

image.png

我們看一下他的實(shí)現(xiàn)類有Jetty、Mock、Tomcat*,Tomcat就不必提了,Jetty略有耳聞和Tomcat并列的容易。

那mock是什么呢,帶著求知的態(tài)度百度一下,沒看懂,過!

image.png

我們還是重點(diǎn)看Tomcat。進(jìn)去看TomcatServletWebServerFactory的實(shí)現(xiàn)類,new了一個(gè)Tomcat的對(duì)象,并做了一些Tomcat的設(shè)置,什么協(xié)議、端口......等等。

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
   if (this.disableMBeanRegistry) {
      Registry.disableRegistry();
   }
   //創(chuàng)建 Tomcat
   Tomcat tomcat = new Tomcat();
   File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
   tomcat.setBaseDir(baseDir.getAbsolutePath());
   // 同步非阻塞io協(xié)議
   Connector connector = new Connector(this.protocol);
   connector.setThrowOnFailure(true);
   tomcat.getService().addConnector(connector);
   customizeConnector(connector);
   tomcat.setConnector(connector);
   tomcat.getHost().setAutoDeploy(false);
   configureEngine(tomcat.getEngine());
   for (Connector additionalConnector : this.additionalTomcatConnectors) {
      tomcat.getService().addConnector(additionalConnector);
   }
   prepareContext(tomcat.getHost(), initializers);
   //這里會(huì)創(chuàng)建 TomcatWebServer 實(shí)例, 并返回
   return getTomcatWebServer(tomcat);
}

好了,到此就把spirng的模板方法onRefresh()在Springboot中是怎么用的說說清楚了,順道把Tomcat是怎么內(nèi)嵌到Springboot中簡(jiǎn)要的講解了一下。

貌似有點(diǎn)跑題了,講onRefresh()方法呢,結(jié)果在springboot中饒了一大圈。不過,能讓讀者更好的理解Spirng和Springboot的關(guān)系,能認(rèn)真的讀讀也是大有裨益的。

也是真的感嘆Spirng作者們的功力之強(qiáng),Spirng的擴(kuò)展性有多少的強(qiáng)大。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容