springIOC原始碼深度解析
以前沒有意識到閱讀優秀框架原始碼的重要性,直到我閱讀完mybatis、spring IOC、AOP,springMVC的原始碼,從中學了不少底層的知識,比如java的反射內省,jdk動態代理,cglib動態代理,體會到原始碼中使用了各種設計模式,使得框架具有非常強大的擴充套件性,那個時候我才發現框架也是很美的。廢話不多說,下面開始我們的SpringIOC原始碼之旅。
本文采用的原始碼版本是5.2.x。為了我們更好地理解springIOC,我們使用的是xml的方式,實際開發中大部分都是是用註解的方式,經驗告訴我,從理解原始碼的角度上來講,xml配置是最好不過了。
閱讀原始碼的建議:去spring官網下載最新的原始碼,對照著看,不然根本看不下去,之前我就吃過虧,自以為看書就能把原始碼弄明白,當時真是太天真了,看了幾頁就放棄了,因為根本就看不懂。
本文假定讀者已經有spring相關的使用基礎,比如如何建立工程,引入spring相關的依賴,並會使用單元測試。
引言
閱讀原始碼最大的難點就是入口難找,經驗告訴我們,我們平時使用到的原始碼方式就是閱讀的入口,這和我們平時開發都是息息相關的。
建一個maven工程,新增相關依賴,使用單元測試來測試獲取bean的流程。
下面我們來看看我們平時都是怎麼使用的springIOC:
下面是我們平時獲取一個bean的原始方式:
public class TestSpring {
@Test
public void testSpring() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml" );
Student student = (Student) ctx.getBean("student");
System.out.println(student);
}
}
複製程式碼
在resource資料夾下新建一個application.xml檔案,通常叫 application.xml 或 application-xxx.xml 就可以了,並配置如下:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="student" class="com.sjc.spring.po.Student">
<!-- String型別 -->
<property name="name" value="jiangcong"></property>
<!-- Integer型別 -->
<property name="age" value="18"></property>
<!-- 引用型別 -->
<property name="course" ref="course"></property>
</bean>
<!-- 該類有一個初始化方法 -->
<bean id="course" class="com.sjc.spring.po.Course"
init-method="init">
<!-- String型別 -->
<property name="name" value="spring"></property>
</bean>
</beans>
複製程式碼
Student類
public class Student {
private String name;
private Integer age;
private Course course;
//... 省略getter/setter方法
}
複製程式碼
Course類
public class Course {
private String name;
//... 省略getter/setter方法
}
複製程式碼
例子很簡單,但是也夠引出本文的主題了,探索spring容器如何加裝配置檔案,以及如何為我們例項化bean,使得我們通過getBean("student")就可以獲取到一個Student類的例項,從而理解spring中核心的IOC、DI。
開始閱讀原始碼之前,先介紹一下Spring重要的介面,這裡不要求大家掌握,只是到時候進入原始碼閱讀部分時候,腦子裡有個印象,再來查詢這部分的知識就好了。
Spring重要介面介紹
BeanFactory繼承體系
看到這些錯綜複雜的類關係圖,我們不禁感慨,spring的龐大。那麼spring為啥要定義這麼多介面呢?因為每個介面都有它使用的場合,各個介面之間具有一定的職責,但是又互不幹擾,這就是設計模式中的介面隔離原則。你就想一下,如果把這些功能全都在一兩個介面實現,那且不是亂成一團糟。
下面介紹主要類的主要功能:
BeanFactory:
介面主要定義了IOC容器的基本行為,比如根據各種條件獲取Bean。這裡使用了工廠模式。
ListableBeanFactory:
從名字可以看出來,這個介面的特點就是可以生產例項列表,比如根據型別獲取Bean例項獲取的是列表Bean例項
HierarchicalBeanFactory:
主要是實現了Bean工廠的分層。
AutowireCapableBeanFactory:
自動裝配的Bean工廠
這個工廠介面繼承自BeanFacotory,它擴充套件了自動裝配的功能,根據類定義BeanDefinition裝配 Bean、執行前、後處理器等。
ConfigurableBeanFactory
複雜的配置Bean工廠
ConfigurableListableBeanFactory
這個類非常之龐大,這個工廠介面總共有83個介面,包含了BeanFactory體系目前的所有方法。
BeanDefinitionRegistry
用來操作定義在工廠內部的BeanDefinition物件。比如註冊BeanDefinition獲取BeanDefinition
BeanDefinition繼承體系
ApplicationContext繼承體系
原始碼解析篇
建立IOC容器
介紹完了主要的介面,我們進入原始碼分析
分析入口是: AbstractApplication#refresh()
整個容器初始化流程大體可以分為12步,想對哪一步驟感興趣的讀者可以自行決定將其作為分支入口瞭解其原理。這裡我分析第2步和第11步,也就是IOC最關鍵的流程:建立BeanFactory的流程和Bean初始化的流程。為了省略篇幅,省略掉了相關幹擾項,比如try/catch塊。
public void refresh() throws BeansException,IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 1: 重新整理預處理
// 設定Spring容器的啟動時間,撤銷關閉狀態,開啟活躍狀態。
// 初始化屬性源資訊(Property)
// 驗證環境資訊裡一些必須存在的屬性
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 2:
// a) 建立IOC容器(DefaultListableBeanFactory)
// b) 載入解析XML檔案(最終儲存到Document物件中)
// c) 讀取Document物件,並完成BeanDefinition的載入和註冊工作
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 3: 對IOC容器做一些預處理(設定一些公共屬性)
prepareBeanFactory(beanFactory);
// 4:
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// 5:呼叫BeanFactoryPostProcessor後置處理器對BeanDefinition處理
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 6: 註冊BeanPostProcessor後置處理器
registerBeanPostProcessors(beanFactory);
// 7: 初始化一些訊息源(比如處理國際化的i18n等訊息資源)
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
// 8: 初始化應用事件廣播器
initApplicationEventMulticaster();
// 9:初始化一些特殊的bean
// Initialize other special beans in specific context subclasses.
onRefresh();
// 10:註冊一些監聽器
// Check for listener beans and register them.
registerListeners();
// 11:例項化剩餘的單例bean(非懶載入方式)
// 注意事項: Bean的IOC、ID和AOP都是發生在此步驟
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// 12: 完成重新整理時,需要釋出對應的事件
// Last step: publish corresponding event.
finishRefresh();
}
//...省略try/catch程式碼塊
複製程式碼
我們跟進第2步obtainFreshBeanFactory(),這裡主要完成XML檔案的解析(最終儲存到Document物件中),讀取Document物件,並完成BeanDefinition的載入和註冊工作,返回一個Bean工廠DefaultListableBeanFactory,讀者的可以對照著上面的UML類關係圖,找到DefaultListableBeanFactory的位置,體會一下這個類的作用。
我們接著進入到AbstractRefreshableApplicationContext#refreshBeanFactory
這裡就會建立一個比較重要的容器IOC容器工廠,DefaultListableBeanFactory,我們配置檔案的資訊就以BeanDefinition物件形式存放在這裡,我們關注loadBeanDefinitions(beanFactory);這行程式碼,這裡就是載入我們的配置檔案封裝到BeanDefinition並存到DefaultListableBeanFactory的邏輯實現,這裡只是定義了一個鉤子方法,實現主要由子類去實現,有點像設計模式中的抽象模板方法。
protected final void refreshBeanFactory() throws BeansException {
// 如果之前有IOC容器,則銷燬
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
// 建立IOC容器,也就是DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 設定工廠的屬性:是否允許BeanDefinition覆蓋和是否允許迴圈依賴
customizeBeanFactory(beanFactory);
// 呼叫BeanDefinition的方法,在當前類中定義了抽象的loadBeanDefinitions方法,具體的實現呼叫子類容器。
loadBeanDefinitions(beanFactory); // 鉤子方法
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
//...省略try/catch程式碼塊
}
複製程式碼
這裡我們主要關心xml配置檔案對應的實現類,也有註解的形式,感興趣的讀者可以將此作為分支進行深入研究。
我們進到 AbstractXmlApplicationContext#loadBeanDefinitions
這裡我們看到了介面隔離設計原則和單一職責原則,初步體會到了定義這麼多的介面的好處。我們先來看BeanFactory的繼承體系中, DefaultListableBeanFactory是實現了BeanDefinitionRegistry介面,擁有了註冊BeanDefinition的能力,但這裡傳給XmlBeanDefinitionReader這個BeanDefinition閱讀器的只是將BeanDefinitionRegistry這個擁有註冊BeanDefinition功能介面傳入(我們看XmlBeanDefinitionReader建構函式就知道,public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) ),其他的能力還在DefaultListableBeanFactory中,這其實是保護了DefaultListableBeanFactory,體現了介面隔離的效果。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException,IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 建立一個BeanDefinition閱讀器,通過閱讀XML檔案,真正完成BeanDefinition的載入和註冊
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
// 委託給BeanDefinitions閱讀器去載入BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
}
複製程式碼
我們接著進入到loadBeanDefinitions方法中,
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException,IOException {
// 獲取資源的定位
// 這裡getConfigResources是一個空實現,真正實現是呼叫子類的獲取資源定位的方法
// 比如:ClassPathXmlApplicationContext中進行了實現
// 而FileSystemXmlApplicationContext沒有使用該方法
Resource[] configResources = getConfigResources();
if (configResources != null) {
// XML Bean讀取器呼叫其父類AbstractBeanDefinitionReader讀取定位的資源
reader.loadBeanDefinitions(configResources);
}
// 如果子類中獲取的資源定位為空,則獲取FileSystemXmlApplicationContext構造方法中setConfigLocations方法設定的資源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// XML Bean讀取器呼叫其父類AbstractBeanDefinitionReader讀取定位的資源
reader.loadBeanDefinitions(configLocations);
}
}
複製程式碼
這裡我們主要看
reader.loadBeanDefinitions(configLocations),並進入到
public int loadBeanDefinitions(String location,@Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 獲取在IOC容器初始化過程中設定的資源載入器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 委派呼叫其子類XmlBeanDefinitionReader的方法,實現載入功能
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources,resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
// ...省略try/catch程式碼塊
}
複製程式碼
我們進入到其子類XmlBeanDefinitionReader#loadBeanDefinitions
這裡會獲取XML檔案的InputStream流,並封裝到InputSource中,我們主要看doLoadBeanDefinitions這個具體的解析過程。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//...省略若干程式碼
// 將資原始檔轉為InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
// 從InputStream中得到XML的解析流
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 具體的解析過程
return doLoadBeanDefinitions(inputSource,encodedResource.getResource());
}
//...省略try/catch程式碼塊
}
複製程式碼
我們來到XmlBeanDefinitionReader#doLoadBeanDefinitions:
這裡主要是將XML封裝成Document物件,然後對Document物件的解析操作,完成BeanDefinition的載入和註冊工作。經過千辛萬苦,我們終於來到這一步,不容易啊!
protected int doLoadBeanDefinitions(InputSource inputSource,Resource resource)
throws BeanDefinitionStoreException {
// 通過DOM4載入解析XML檔案,最終形成Document物件
Document doc = doLoadDocument(inputSource,resource);
// 通過對Document物件的解析操作,完成BeanDefinition的載入和註冊工作
int count = registerBeanDefinitions(doc,resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
// .../省略try/catch程式碼塊
複製程式碼
我們來看看BeanDefinition是如何註冊的
進入到XmlBeanDefinitionReader#registerBeanDefinitions
這裡 很好地利用了面向物件中單一職責原則,將邏輯處理委託給單一的類進行處理,比如: BeanDefinitionDocumentReader
public int registerBeanDefinitions(Document doc,Resource resource) throws BeanDefinitionStoreException {
// 使用DefaultBeanDefinitionDocumentReader例項化BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 記錄統計前BeanDefinition的載入個數
int countBefore = getRegistry().getBeanDefinitionCount();
// 載入及註冊bean
documentReader.registerBeanDefinitions(doc,createReaderContext(resource));
// 記錄本次載入的BeanDefinition個數
return getRegistry().getBeanDefinitionCount() - countBefore;
}
複製程式碼
我們進入到BeanDefinitionDocumentReader#registerBeanDefinitions
並進入到其預設實現類DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
// 這裡使用了委託模式,將具體的BeanDefinition解析工作交給了BeanDefinitionParserDelegate去完成
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(),root,parent);
//...省略掉了預設名稱空間的程式碼
// 在解析Bean定義之前,進行自定義的解析,增強解析過程的可擴充套件性
preProcessXml(root); // 鉤子方法
// 委託給BeanDefinitionParserDelegate,從Document的根元素開始進行BeanDefinition的解析
parseBeanDefinitions(root,this.delegate);
// 在解析Bean定義之後,進行自定義的解析,增加解析過程的可擴充套件性
postProcessXml(root); // 鉤子方法
this.delegate = parent;
}
複製程式碼
這裡我們主要關心parseBeanDefinitions
進入到DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
這裡就比較重要了,遍歷解析Document物件的所有子節點,這裡我們只關心parseDefaultElement,也就是 bean標籤、import標籤、alias標籤,則使用預設解析規則
protected void parseBeanDefinitions(Element root,BeanDefinitionParserDelegate delegate) {
// 載入的Document物件是否使用了Spring預設的XML名稱空間(beans名稱空間)
if (delegate.isDefaultNamespace(root)) {
// 獲取Document物件根元素的所有子節點(bean標籤、import標籤、alias標籤和其他自定義標籤context、aop等)
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// bean標籤、import標籤、alias標籤,則使用預設解析規則
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele,delegate);
}
else { //像context標籤、aop標籤、tx標籤,則使用使用者自定義的解析規則解析元素節點
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
複製程式碼
我們進入到DefaultBeanDefinitionDocumentReader#parseDefaultElement
private void parseDefaultElement(Element ele,BeanDefinitionParserDelegate delegate) {
// 解析<import>標籤
if (delegate.nodeNameEquals(ele,IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 解析<alias>標籤
else if (delegate.nodeNameEquals(ele,ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 解析bean標籤
else if (delegate.nodeNameEquals(ele,BEAN_ELEMENT)) {
processBeanDefinition(ele,delegate);
}
else if (delegate.nodeNameEquals(ele,NESTED_BEANS_ELEMENT)) {
// recurse
// 遞迴呼叫
doRegisterBeanDefinitions(ele);
}
}
複製程式碼
這裡我們主要看標籤的解析,其他的讀者感興趣的話可以自己去看。
我們進入到DefaultBeanDefinitionDocumentReader#processBeanDefinition
BeanDefinitionParserDelegate#parseBeanDefinitionElement會解析BeanDefinition,並封裝到BeanDefinitionHolder,
BeanDefinitionReaderUtils#registerBeanDefinition會最終將BeanDefinition註冊到BeanDefinitionRegistry(DefaultListableBeanFactory)中,並存入到map中:private final Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
感興趣的讀者可以繼續跟蹤,這裡我們終於解析完了BeanDefinition。完成了AbstractApplicationContext#refresh() 的第2步:XML檔案的解析和BeanDefinition的載入和註冊工作
protected void processBeanDefinition(Element ele,BeanDefinitionParserDelegate delegate) {
// 解析<bean>標籤,獲取BeanDefinition
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele,bdHolder);
try {
// 註冊最終的BeanDefinition到BeanDefinitionRegistry(DefaultListableBeanFactory)
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'",ele,ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
複製程式碼
例項化Bean
我們來看看AbstractApplicationContext#refresh()的第11步,這步很重要、很重要、很重要。。。
這裡完成了單例Bean的例項化,Bean的IOC、ID和AOP都是發生在這步
AbstractApplicationContext#finishBeanFactoryInitialization
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//... 省略掉了若干程式碼
// Instantiate all remaining (non-lazy-init) singletons.
// 例項化單例bean
beanFactory.preInstantiateSingletons();
}
複製程式碼
我們進入到DefaultListableBeanFactory#preInstantiateSingletons
這裡先了解一下,BeanFactory和FactoryBean的區別,
-
BeanFactory是spring頂級介面,是spring基礎容器,它負責管理bean例項。
-
FactoryBean只是spring容器中被管理的一個bean物件,只是說這個bean它的能力就是產生另外的物件。
-
BeanFactory是一個包容萬物的大工廠
-
FactoryBean是一個只能生產指定物件的小工廠,而且這個小工廠還被大工廠給管理。
-
FactoryBean和普通的Bean例項,被Spring管理時,也是區別對待的。通過&字首來區分FactoryBean和普通的Bean例項
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.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
// 觸發所有非懶載入方式的單例bean的建立
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 如果bean是一個FactoryBean,則走下面的方法
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
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);
}
}
}
//...省略掉若干程式碼
}
複製程式碼
這裡我們走的是普通的Bean
進入AbstractBeanFactory#getBean,
並進入到AbstractBeanFactory#doGetBean
這個方法,主要分為兩個步驟:
- 從快取中獲取單例Bean,如果獲取到了就檢測,如果獲取出來的Bean是FactoryBean,則需要從FactoryBean例項中產生一個物件
- 如果沒有獲取到Bean,則需要通過BeanDefinition來例項化一個Bean返回
protected <T> T doGetBean(final String name,@Nullable final Class<T> requiredType,@Nullable final Object[] args,boolean typeCheckOnly) throws BeansException {
// 獲取bean的名稱
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 從快取中獲取單例bean
Object sharedInstance = getSingleton(beanName);
// 如果獲取到單例bean,則走下面程式碼
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 如果取出來的Bean例項是FactoryBean的Bean例項,則需要從FactoryBean例項中產生一個物件例項
bean = getObjectForBeanInstance(sharedInstance,name,beanName,null);
}
else { // 如果沒有獲取到單例bean,則走下面程式碼
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// 如果原型模式的Bean發生迴圈引用,則直接不處理,直接拋異常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//...省略了檢測BeanDefinition是否在Factory中的程式碼
try {
// 獲取例項化的bean的BeanDefinition物件
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 檢查該BeanDefinition物件對應的Bean是否是抽象的
checkMergedBeanDefinition(mbd,args);
//...省略檢測程式碼
// Create bean instance.
// 如果是單例的Bean,看下面程式碼
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName,() -> {
try {
// 建立單例Bean的主要方法
return createBean(beanName,mbd,args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process,to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance,mbd);
}
else if (mbd.isPrototype()) { // 原型
//...省略了處理原型模式Bean的分支
}
else {
//...省略了比如處理request、session級別的bean的分支
}
return (T) bean;
}
複製程式碼
我們主要來看這段程式碼(重要):
這裡用到了Java8的lambda表示式
// Create bean instance.
// 如果是單例的Bean,看下面程式碼
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName,mbd);
}
複製程式碼
進入到DefaultSingletonBeanRegistry#getSingleton
這裡處理了迴圈依賴的問題,什麼是迴圈依賴,以及spring如何解決迴圈依賴,期待我下一篇文章。
public Object getSingleton(String beanName,ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//...省略檢測程式碼
// 建立之前,設定一個建立中的標識
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 呼叫匿名內部類獲取單例物件
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
//...省略catch程式碼塊
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 消除物件建立中的標識
afterSingletonCreation(beanName);
}
// 將產生的單例Bean放入快取中(總共三級快取)
if (newSingleton) {
addSingleton(beanName,singletonObject);
}
}
return singletonObject;
}
}
複製程式碼
上面 singletonObject = singletonFactory.getObject();呼叫的是lambda表示式中的createBean(beanName,args)方法。
我們進入到其實現類
AbstractAutowireCapableBeanFactory#createBean
protected Object createBean(String beanName,RootBeanDefinition mbd,@Nullable Object[] args)
throws BeanCreationException {
//...省略若干程式碼塊
// 完成Bean例項的建立(例項化、填充屬性、初始化)
Object beanInstance = doCreateBean(beanName,mbdToUse,args);
}
複製程式碼
進入到我們關心的
AbstractAutowireCapableBeanFactory#doCreateBean
這裡完成完成Bean例項的建立,包括三個步驟:
-
例項化
預設呼叫無參構造例項化Bean,構造引數依賴注入就是發生在這個步驟
-
屬性填充(DI依賴注入發生在此步驟)
利用反射和內省技術進行屬性設定
-
初始化(AOP發生在此步驟)
也是利用反射呼叫初始化方法比如
protected Object doCreateBean(final String beanName,final RootBeanDefinition mbd,final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
// bean初始化第一步:預設呼叫無參構造例項化Bean
// 構造引數依賴注入,就是發生在這一步
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName,args);
}
// 例項化後的Bean物件
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
//...省略若干程式碼
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 解決迴圈依賴的關鍵步驟
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 如果需要提前暴露單例Bean,則將該Bean放入三級快取中
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 將剛建立的bean放入三級快取中singleFactories(key是beanName,value是FactoryBean)
addSingletonFactory(beanName,() -> getEarlyBeanReference(beanName,bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// bean初始化第二步: 填充屬性(DI依賴注入發生在此步驟)
// 這裡主要分為兩種情況:
// 1.對於非集合型別的屬性,直接使用反射和內省機制去進行屬性設定
// 2。對於集合型別的屬性,將其屬性值解析為目標型別的集合後直接賦值給屬性
populateBean(beanName,instanceWrapper);
// bean初始化第三步:呼叫初始化方法,完成bean的初始化操作(AOP發生在此步驟)
exposedObject = initializeBean(beanName,exposedObject,mbd);
}
//... 省略catch塊程式碼
//...省略若干程式碼
return exposedObject;
}
複製程式碼
到這裡,SpringIOC容器對Bean定義的資原始檔的載入、解析和依賴注入已經解析完畢,現在SpringIOC容器中管理了一系列靠依賴關係聯絡起來的Bean,程式不需要應用自己手動建立所需物件,SpringIOC容器會在我們使用的時候自動為我們建立,並且為我們注入好相關的依賴,這就是Spring核心功能的控制反轉和依賴注入的相關功能,所謂的反轉,也就是將建立Bean的權利交給spring進行管理。