Spring擴充套件點探索之prepareRefresh

只需低頭努力,剩下的交給時光,時間會公平地幫你處理這一切~

前言

Spring作為優秀的框架,被很多開發者使用,只要是做Java開發的,就一定會知道Spring框架。

Spring作為Java開發框架的標配,它所表現出來的優秀特性大大降低了企業開發過程中的複雜性

在前面我通過幾篇原始碼的文章,分析了Spring是如何把Bean註冊到容器中的、Spring是如何獲取容器中的Bean的、Spring是如何實現AOP的

原始碼分析:Spring是如何把Bean註冊到IOC容器中的?

原始碼分析:Spring是如何獲取容器中的Bean?

原始碼分析:Spring是如何實現AOP的?

有興趣可以去了解一下

那Spring這麼優秀的框架,在擴充套件性方面也是極好的,我們知道Spring整個框架是個很龐大的集體,裡面包括很多東西

Spring容器在啟動過程中,有很多步驟,如初始化、載入Bean、國際化、事件廣播等,在很多步驟中Spring都預留了介面給你來進行擴充套件,讓你在使用Spring框架的過程中,可以做很多個性化的操作

從這個層面來講,我們在使用Spring框架的時候,不光是把Bean物件交給Spring來管理,你也可以介入進去,可以干涉註冊到Spring容器中的Bean

下面我們就一起來探索一下,Spring有哪些擴充套件點?

refresh()方法

瞭解過Spring原始碼的朋友肯定知道refresh()方法,如果連這個方法都不知道,那你百分之百沒有看過Spring原始碼

refresh()方法可以說是Spring框架的核心,它串起了整個Spring容器的啟動過程,你把這個方法裡面的邏輯搞清楚了,那你對Spring的整體脈絡就會有一個清晰的認識

下面我們就來看一下refresh()方法

public void refresh() throws BeansException, IllegalStateException { synchronized (this。startupShutdownMonitor) { // Prepare this context for refreshing。 //容器啟動前的準備工作 prepareRefresh(); // Tell the subclass to refresh the internal bean factory。 //告訴子類重新整理內部Bean工廠,解析Bean並註冊到容器中(此時還沒有初始化) ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context。 //準備BeanFactory,設定beanFactory類載入器,新增多個beanPostProcesser prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses。 //允許子類上下文中對beanFactory做後期處理 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context。 //呼叫BeanFactoryPostProcessor各個實現類的方法 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation。 // 註冊 BeanPostProcessor 的實現類 registerBeanPostProcessors(beanFactory); // Initialize message source for this context。 // 初始化ApplicationContext的MessageSource initMessageSource(); // Initialize event multicaster for this context。 // 初始化事件廣播 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses。 //初始化子類特殊bean onRefresh(); // Check for listener beans and register them。 //註冊事件監聽器 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons。 // 初始化所有singleton bean finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event。 // 廣播事件,ApplicationContext初始化完成 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(); } } }

方法主體總共12個方法

prepareRefresh()

容器啟動前的準備工作

obtainFreshBeanFactory()

告訴子類重新整理內部Bean工廠,解析Bean並註冊到容器中(此時還沒有初始化)

prepareBeanFactory(beanFactory)

準備BeanFactory,設定beanFactory類載入器,新增多個beanPostProcesser

postProcessBeanFactory(beanFactory)

允許子類上下文中對beanFactory做後期處理

invokeBeanFactoryPostProcessors(beanFactory)

呼叫BeanFactoryPostProcessor各個實現類的方法

registerBeanPostProcessors(beanFactory)

註冊 BeanPostProcessor 的實現類

initMessageSource()

初始化ApplicationContext的MessageSource

initApplicationEventMulticaster()

初始化事件廣播

onRefresh()

初始化子類特殊bean

registerListeners()

註冊事件監聽器

finishBeanFactoryInitialization(beanFactory)

初始化所有singleton bean

finishRefresh()

廣播事件,ApplicationContext初始化完成

大部分方法都是為了完成註冊和初始化,整個refresh()方法執行完成之後,基本上容器就啟動完成了

prepareRefresh()方法

我們今天先來看一下prepareRefresh()方法,它是容器啟動之前準備工作

protected void prepareRefresh() { // 記錄開始啟動的時間 this。startupDate = System。currentTimeMillis(); //設定關閉狀態為false this。closed。set(false); //設定啟用狀態為true this。active。set(true); if (logger。isDebugEnabled()) { if (logger。isTraceEnabled()) { logger。trace(“Refreshing ” + this); } else { logger。debug(“Refreshing ” + getDisplayName()); } } // Initialize any placeholder property sources in the context environment。 //初始化上下文環境中的佔位符 initPropertySources(); // Validate that all properties marked as required are resolvable: // see ConfigurablePropertyResolver#setRequiredProperties //校驗所有必須的屬性 getEnvironment()。validateRequiredProperties(); // Store pre-refresh ApplicationListeners。。。 if (this。earlyApplicationListeners == null) { this。earlyApplicationListeners = new LinkedHashSet<>(this。applicationListeners); } else { // Reset local application listeners to pre-refresh state。 this。applicationListeners。clear(); this。applicationListeners。addAll(this。earlyApplicationListeners); } // Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available。。。 this。earlyApplicationEvents = new LinkedHashSet<>(); }

這裡就出現了第一個擴充套件點,我們可以看到initPropertySources()是空的

protected void initPropertySources() { // For subclasses: do nothing by default。 }

在Spring裡面有很多這種空的方法,很多人在看原始碼的時候可能不太理解,為什麼會呼叫一個空的方法,其實這是Spring為你預留的口子,就是讓你自由發揮的地方

既然initPropertySources()是空的,那我們就來自己實現一下

public class MyAnnotationConfigApplicationContext extends AnnotationConfigApplicationContext { public MyAnnotationConfigApplicationContext(String。。。 basePackages){ super(basePackages); } @Override protected void initPropertySources() { super。initPropertySources(); getEnvironment()。getSystemProperties()。put(“action”,“run”); getEnvironment()。setRequiredProperties(“KEY”); }}

這裡我繼承了AnnotationConfigApplicationContext,實現了initPropertySources()方法,在裡面添加了一個系統屬性,並且標記“KEY”為必須的引數

下面我們來測試一下

public class AnnotionBeanTest { public static void main(String[] args){ MyAnnotationConfigApplicationContext applicationContext = new MyAnnotationConfigApplicationContext(“org。kxg。springDemo”); }}

啟動後發現報如下的錯誤:

Exception in thread “main” org。springframework。core。env。MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [KEY] at org。springframework。core。env。AbstractPropertyResolver。validateRequiredProperties(AbstractPropertyResolver。java:146) at org。springframework。core。env。AbstractEnvironment。validateRequiredProperties(AbstractEnvironment。java:523) at org。springframework。context。support。AbstractApplicationContext。prepareRefresh(AbstractApplicationContext。java:603) at org。springframework。context。support。AbstractApplicationContext。refresh(AbstractApplicationContext。java:518) at org。springframework。context。annotation。AnnotationConfigApplicationContext。(AnnotationConfigApplicationContext。java:101) at org。kxg。springDemo。refresh。prepare。MyAnnotationConfigApplicationContext。(MyAnnotationConfigApplicationContext。java:8) at org。kxg。springDemo。AnnotionBeanTest。main(AnnotionBeanTest。java:8)

從異常資訊可以看到,這裡是缺少必須引數的Exception,這裡可以用來驗證系統啟動的一些必須引數

我們把“KEY”加到系統引數中,再來看一下

public class AnnotionBeanTest { public static void main(String[] args){ System。setProperty(“KEY”,“VAL”); MyAnnotationConfigApplicationContext applicationContext = new MyAnnotationConfigApplicationContext(“org。kxg。springDemo”); }}

啟動容器,一切正常,至此我們就完成了第一個Spring的擴充套件點,當然例子中的擴充套件很簡單,實際專案中大家可以根據情況來做自己的擴充套件,後面會陸續介紹其他的擴充套件點,請大家持續關注!