Spring框架中的IoC容器源碼分析(下)
上一篇:我的架構夢:(五)Spring框架中的IoC容器源碼分析(上)
四、BeanFactory創建流程
1、獲取BeanFactory子流程
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
...
@Override
public void refresh() throws BeansException, IllegalStateException {
/**
* 對象鎖加鎖
*/
synchronized (this.startupShutdownMonitor) {
...
// 我這裏直接定位到該流程需要的代碼,其他的無關代碼省略了哈
/**
* 第二步:
*
* 獲取BeanFactory:默認實現是DefaultListableBeanFactory
* Bean獲取並封裝成BeanDefinition對象
* 加載BeanDefinition 並註冊到BeanDefinitionRegistry
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
...
}
}
...
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
...
/**
* 對BeanFactory執行刷新操作,關閉以前的BeanFactory(如果有)
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
// 判斷是否已有BeanFactory
if (hasBeanFactory()) {
// 銷燬Beans
destroyBeans();
// 關閉BeanFactory
closeBeanFactory();
}
try {
// 實例化DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 設置序列化id
beanFactory.setSerializationId(getId());
// 自定義bean工廠的一些屬性(是否覆蓋、是否允許循環依賴)
customizeBeanFactory(beanFactory);
// 加載應用中的BeanDefinitions
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
// 賦值當前BeanFactory
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
...
}
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
...
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return this.beanFactory;
}
}
...
}
2、BeanDefinition加載解析及註冊子流程
2.1 該子流程涉及到如下幾個關鍵步驟
Resource定位:指對BeanDefinition
的資源定位過程。通俗講就是找到定義Javabean
信息的XML
文 件,並將其封裝成Resource
對象。
BeanDefinition載入 :把用戶定義好的Javabean
表示爲IoC
容器內部的數據結構,這個容器內部的數據結構就是BeanDefinition
。
2.2 源碼過程分析
Step 1:子流程入口在我們寫的測試類的ClassPathXmlApplicationContext
構造方法中
Step 2:繼續跟進 ClassPathXmlApplicationContext
#ClassPathXmlApplicationContext
方法中
Step 3:繼續跟進 AbstractRefreshableApplicationContext
#refreshBeanFactory
方法中
Step 4:繼續跟進 AbstractApplicationContext
#obtainFreshBeanFactory
#refreshBeanFactory
方法中
Step 5:依次調用多個類的 loadBeanDefinitions
方法 —> AbstractXmlApplicationContext
—> AbstractBeanDefinitionReader
—> XmlBeanDefinitionReader
一直執行到 XmlBeanDefinitionReader
的 doLoadBeanDefinitions
方法
Step 6:我們重點觀察XmlBeanDefinitionReader
類的 registerBeanDefinitions
方法,期間產生了多 次重載調用,我們定位到最後一個。
此處我們關注兩個地方:一個createRederContext
方法,一個是 DefaultBeanDefinitionDocumentReader
類的registerBeanDefinitions
方法,先進入 createRederContext
方法看看。
我們可以看到,此處 Spring
首先完成了 NamespaceHandlerResolver
的初始化。
我們再進入 registerBeanDefinitions
方法中追蹤,調用了
DefaultBeanDefinitionDocumentReader
#registerBeanDefinitions
方法。
進入 parseBeanDefinitions
方法:
進入 parseDefaultElement
方法:
進入 processBeanDefinition
方法:
至此,註冊流程結束,我們發現,所謂的註冊就是把封裝的 XML
中定義的 Bean
信息封裝爲 BeanDefinition
對象之後放入一個Map
中,BeanFactory
是以 Map
的結構組織這些 BeanDefinition
的。
Step 6:繼續跟進BeanDefinitionReaderUtils
#registerBeanDefinition
方法
Step 7:繼續跟進DefaultListableBeanFactory
#registerBeanDefinition
方法
可以在DefaultListableBeanFactory
中看到此Map
的定義:
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
五、Bean創建流程
1、通過最開始的關鍵時機點分析,我們知道Bean
創建子流程入口在
AbstractApplicationContext
#refresh()
方法的finishBeanFactoryInitialization(beanFactory)
處
2、進入finishBeanFactoryInitialization
方法
3、繼續進入DefaultListableBeanFactory
類的preInstantiateSingletons
方法,我們找到下面部分的 代碼,看到工廠Bean
或者普通Bean
,最終都是通過getBean
的方法獲取實例。
4、繼續跟蹤下去,我們進入到了AbstractBeanFactory
類的doGetBean
方法,這個方法中的代碼很多,我們直接找到核心部分。
5、接着進入到AbstractAutowireCapableBeanFactory
類的方法,找到以下代碼部分。
進入doCreateBean
方法看看,該方法我們關注兩塊重點區域:
-
創建Bean實例,此時尚未設置屬性。
if (instanceWrapper == null) { // 創建Bean實例,僅僅調用構造方法,但是尚未設置屬性 instanceWrapper = createBeanInstance(beanName, mbd, args); }
-
給Bean填充屬性,調用初始化方法,應用BeanPostProcessor後置處理器。
try { // Bean屬性填充 populateBean(beanName, mbd, instanceWrapper); // 調用初始化方法,應用BeanPostProcessor後置處理器 exposedObject = initializeBean(beanName, exposedObject, mbd); }
這樣我們就完成了Bean創建流程的源碼分析。
六、lazy-init 延遲加載機制原理
普通 Bean
的初始化是在容器啓動初始化階段執行的,而被lazy-init=true
修飾的 bean
則是在從容器裏第一次進行context.getBean()
時進行觸發。Spring
啓動的時候會把所有bean
信息(包括XML和註解)解析轉化成Spring
能夠識別的BeanDefinition
並存到Hashmap
裏供下面的初始化時用,然後對每個 BeanDefinition
進行處理,如果是懶加載的則在容器初始化階段不處理,其他的則在容器初始化階段進 行初始化並依賴注入。
1、入口AbstractApplicationContext
#finishBeanFactoryInitialization
/**
* Finish the initialization of this context's bean factory,
* initializing all remaining singleton beans.
*
* 結束BeanFactory的初始化工作
* 實例化所有單例bean
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
// 實例化所有立即加載的單例bean
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
// 實例化所有立即加載的單例bean
beanFactory.preInstantiateSingletons();
}
2、跟進DefaultListableBeanFactory
#preInstantiateSingletons
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
// 所有bean的名字
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
// 觸發所有非延遲加載單例bean的初始化,主要步驟爲getBean
for (String beanName : beanNames) {
// 合併父BeanDefinition對象
// map.get(beanName)
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
// 如果是FactoryBean則加&
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
// 實例化當前bean
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
3、小結
- 對於被修飾爲
lazy-init
的bean
,Spring
容器初始化階段不會進行init
並且依賴注入,當第一次進行getBean
時候才進行初始化並依賴注入。 - 對於非懶加載的
bean
,getBean
的時候會從緩存裏頭獲取,因爲容器初始化階段Bean
已經初始化完成並緩存了起來。
七、Spring IoC 循環依賴問題
1、什麼是循環依賴
循環依賴其實就是循環引用,也就是兩個或者兩個以上的 Bean 互相持有對方,最終形成閉環。比如A 依賴於B,B依賴於C,C又依賴於A。
注意,這裏不是函數的循環調用,是對象的相互依賴關係。循環調用其實就是一個死循環,除非有終結 條件。
Spring中循環依賴場景有:
- 構造器的循環依賴(構造器注入)
- Field 屬性的循環依賴(set注入)
其中,構造器的循環依賴問題無法解決,只能拋出 BeanCurrentlyInCreationException
異常,在解決
屬性循環依賴時,spring
採用的是提前暴露對象的方法。
2、循環依賴處理機制
-
單例
bean
構造器參數循環依賴(無法解決) -
prototype
原型bean
循環依賴(無法解決)
對於原型bean的初始化過程中不論是通過構造器參數循環依賴還是通過setXxx方法產生循環依 賴,Spring都 會直接報錯處理。
AbstractBeanFactory.doGetBean()方法:if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
protected boolean isPrototypeCurrentlyInCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); }
在獲取bean之前如果這個原型bean正在被創建則直接拋出異常。原型bean在創建之前會進行標記 這個beanName正在被創建,等創建結束之後會刪除標記。
try { //創建原型bean之前添加標記 beforePrototypeCreation(beanName); //創建原型bean prototypeInstance = createBean(beanName, mbd, args); } finally { //創建原型bean之後刪除標記 afterPrototypeCreation(beanName); }
總結:Spring 不支持原型 bean 的循環依賴。
-
單例
bean
通過setXxx
或者@Autowired
進行循環依賴Spring
的循環依賴的理論依據基於Java
的引用傳遞,當獲得對象的引用時,對象的屬性是可以延 後設置的,但是構造器必須是在獲取引用之前。Spring
通過setXxx
或者@Autowired
方法解決循環依賴其實是通過提前暴露一個ObjectFactory
對象來完成的,簡單來說ClassA
在調用構造器完成對象初始化之後,在調用ClassA
的setClassB
方法 之前就把ClassA
實例化的對象通過ObjectFactory
提前暴露到Spring
容器中。Spring
容器初始化ClassA
通過構造器初始化對象後提前暴露到Spring
容器。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}
//將初始化後的對象提前已ObjectFactory對象注入到容器中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
3、循環依賴整體流程
ClassA
調用setClassB
方法,Spring
首先嚐試從容器中獲取ClassB
,此時ClassB
不存在Spring
容器中。Spring
容器初始化ClassB
,同時也會將ClassB
提前暴露到Spring
容器中。ClassB
調用setClassA
方法,Spring
從容器中獲取ClassA
,因爲第一步中已經提前暴露了。
ClassA
,因此可以獲取到ClassA
實例。
ClassA
通過spring
容器獲取到ClassB
,完成了對象初始化操作。- 這樣
ClassA
和ClassB
都完成了對象初始化操作,解決了循環依賴問題。
Spring框架中的IoC容器源碼分析到此總算分析完了,如果覺得對你有幫助的話點個贊再走吧,對的起博主奮戰到凌晨兩點寫完的。。。