Spring原始碼之六-onRefresh()方法

程式設計師田同學發表於2022-03-04

Spring原始碼之六-onRefresh()方法

大家好,我是程式設計師田同學。

今天帶大家解讀Spirng原始碼之六的onRefresh()方法,這是refresh()的其中的一個方法,看似是一個空方法,實則他是非常非常重要的,對於提高Spring的擴充套件性。

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

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

			// Tell the subclass to refresh the internal bean factory.
			//2、將會初始化 BeanFactory、載入 Bean、註冊 Bean
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			//3、設定 BeanFactory 的類載入器,新增幾個 BeanPostProcessor,手動註冊幾個特殊的 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.
				//執行BeanFactory後置處理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// 5、Register bean processors that intercept bean creation.
				//註冊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實現了這個方法
				onRefresh();

				// Check for listener beans and register them.
				//7、註冊監聽器
				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()的主要作用,那麼文章到這裡就結束了,感謝閱讀!

開玩笑,只說作用不舉例那和耍流氓沒有什麼區別,接下來就以Spirng的典型實現Springboot來舉例。

該方法的執行時機是Spring已經載入好了一些特殊的bean(內建的一些bean,實現了bean工廠後置處理器的類)之後,在例項化單例bean之前。讓我們來看Springboot是怎麼呼叫這個模板方法的。

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

點選檢視Springboot的onRresh()的實現方法。

有兩個包路徑含有boot的,一定就是Spirngboot的實現方法。

image-20220304092825904

這是Spirng的onRresh()的實現方法。

image-20220304092916345

比對一下Spirng的onRresh()和SpirngbootRefersh的實現類對比,Springboot多了兩個實現類,ReactiveWebServerApplicationContext類和ServletWebServerApplicationContext類。

我們分別檢視這兩個實現的onRresh()方法都做了什麼?

方法名都是createWebServer()方法,以為這兩個方法都是一個方法,仔細一看發現並不是。

image-20220304091218953

兩個createWebServer()方法做了什麼呢?我們debug進去摟一眼。

ReactiveWebServerApplicationContext類的onRresh()方法並沒有執行到,見名知意應該是跟webServer管理相關的,限於篇幅問題,留個坑暫時放在吧。

image-20220304094147835

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

	private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		//第一次進來webServer servletContext都是null,會進到if分支裡面
		if (webServer == null && servletContext == null) {
			//這裡就會來查詢ServletWebServerFactory,也就是web容器的工廠,具體看下getWebServerFactory()方法,
			// 還是ServletWebServerApplicationContext這個類的方法
			//建立了 TomcatServletWebServerFactory 類
			ServletWebServerFactory factory = getWebServerFactory();
			//建立 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();
	}

核心應該是 factory.getWebServer(getSelfInitializer()),這個方法是建立了一個容器。都有哪些容器呢?

image-20220304095354416

我們看一下他的實現類有Jetty、Mock、Tomcat*,Tomcat就不必提了,Jetty略有耳聞和Tomcat並列的容易。

那mock是什麼呢,帶著求知的態度百度一下,沒看懂,過!

image-20220304095550423

我們還是重點看Tomcat。進去看TomcatServletWebServerFactory的實現類,new了一個Tomcat的物件,並做了一些Tomcat的設定,什麼協議、埠......等等。

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
   if (this.disableMBeanRegistry) {
      Registry.disableRegistry();
   }
   //建立 Tomcat
   Tomcat tomcat = new Tomcat();
   File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
   tomcat.setBaseDir(baseDir.getAbsolutePath());
   // 同步非阻塞io協議
   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);
   //這裡會建立 TomcatWebServer 例項, 並返回
   return getTomcatWebServer(tomcat);
}

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

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

也是真的感嘆Spirng作者們的功力之強,Spirng的擴充套件性有多少的強大。

相關文章