spring源碼學(xué)習(xí)筆記,要點(diǎn)歸納和代碼理解
前言
經(jīng)過了三節(jié)的的鋪墊,終于到了對象創(chuàng)建的核心部分了,bean的整個加載過程代碼量非常大,邏輯錯綜復(fù)雜,因此這部分我的原則是以理解和把控關(guān)鍵過程和邏輯為基本,不局限于細(xì)節(jié)實(shí)現(xiàn),以AbstractBeanFactory的doGetBean方法為骨架閱讀抽象出的方法代碼,對于重點(diǎn)邏輯詳細(xì)解析.
bean加載的整體流程
首先貼出精簡了異常處理邏輯的doGetBean方法代碼
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 轉(zhuǎn)換beanName,將alias或id或className統(tǒng)一轉(zhuǎn)換為beanName
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 從緩存中嘗試獲取實(shí)例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 緩存中取出的不一定是創(chuàng)建好的實(shí)例,有可能是提早曝光加入到緩存的factoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// 當(dāng)前bean如果在創(chuàng)建過程中,則發(fā)生循環(huán)依賴,拋出異常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
// 檢測父類工廠,此處為了保證bean的父類能夠被先于子實(shí)例加載
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// 標(biāo)記已創(chuàng)建
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
getBean(dep);
}
}
// Create bean instance.
if (mbd.isSingleton()) { // 創(chuàng)建單例bean
sharedInstance = getSingleton(beanName, () -> {
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) { // 創(chuàng)建多例
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
}
// Check if required type matches the type of the actual bean instance.
// 類型檢查
if (requiredType != null && !requiredType.isInstance(bean)) {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
return (T) bean;
}
整個加載過程的步驟大致如下:
- 轉(zhuǎn)換對應(yīng)的beanName
- 嘗試從緩存中加載實(shí)例
- bean的實(shí)例化
- prototype的依賴檢查
- 檢測parentBeanFactory
- 將GernericBeanDefinition 轉(zhuǎn)換為RootBeanDefinition
- 尋找依賴
- 針對不同scope創(chuàng)建bean
- 類型轉(zhuǎn)換
循環(huán)依賴的處理
- 首先解釋下什么是循環(huán)依賴:
對于兩個class A和B,以及他們對應(yīng)的bean a和b,A中有類型為B的屬性b,B中有類型為A的屬性a,兩個對象都通過spring管理并且屬性都通過spring自動注入,此時便形成了循環(huán)依賴. - spring對于循環(huán)依賴的解決:
整體思路是:
- 使用factoryBean封裝創(chuàng)建對象的邏輯
- 將正在創(chuàng)建的bean對應(yīng)的factoryBean暴露到緩存中
- 創(chuàng)建bean時先嘗試從幾個緩存中取出bean或bean的factoryBean
主要應(yīng)用到四個對象和兩個方法:
- singletonObjects: 保存創(chuàng)建的bean和beanName的關(guān)系
- singletonFactories: 保運(yùn)BeanName和創(chuàng)建bean的工廠之間的關(guān)系
- earlySingletonObjects: 保存beanName和bean實(shí)例之間的關(guān)系,與singletonObjects的區(qū)別是,當(dāng)一個單例bean被放到這里后,那么即使bean還在創(chuàng)建過程中,也可以通過getBean方法從這個緩存中獲取
- registeredSingletons: 用來保存的當(dāng)前所有已注冊的bean
DefaultSingletonBeanRegistry中的兩個方法
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
- 具體步驟:
- spring容器創(chuàng)建單例TestBeanA, 根據(jù)無參構(gòu)造器創(chuàng)建,并通過ObjectFactory接口暴露getEarlyBeanReference()方法,將beanName和封裝該方法的ObjectFactory添加到singletonFactory緩存中
- 在執(zhí)行A的populateBean方法,即裝配A的屬性時,發(fā)現(xiàn)B的依賴,執(zhí)行g(shù)etBean(B)方法
- 創(chuàng)建單例B,將B的ObjectFactory加入到singletonFactory,之后繼續(xù)執(zhí)行B的populateBean,
- 發(fā)現(xiàn)依賴注入中存在TestBeanA,調(diào)用getBean(A),這時會通過getSingle(A)的方法調(diào)用到之前暴露的getEarlyBeanReference方法,這個方法如下,會返回a的實(shí)例.
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
這里附一張調(diào)用棧的截圖,一目了然.

- 需要注意的:
spring只會針對singleton的setter注入解決循環(huán)依賴,對于構(gòu)造器循環(huán)依賴和prototype的循環(huán)依賴會直接拋出異常,這是因?yàn)閟pring只緩存single scope的bean,也就是說上述代碼邏輯對prototype不適用.而spring將在創(chuàng)建過程中的bean放入一個threadLocal的prototypesCurrentlyInCreation對象中,通過構(gòu)造器注入的對象會無法通過此處校驗(yàn)而拋出異常.
可以通過((XmlBeanFactory) bf).setAllowCircularReferences(false);來禁用循環(huán)引用
創(chuàng)建bean
創(chuàng)建bean的實(shí)現(xiàn)通過lambda表達(dá)式將AbstractBeanFactory的doGetBean方法封裝到factoryBean中進(jìn)行的.
- 主要步驟:
- 創(chuàng)建bean的實(shí)例
a. 確定構(gòu)造函數(shù)
b. 判斷beanDefinition中的getMethodOverrides,決定直接反射構(gòu)造bean還是通過動態(tài)代理的方式構(gòu)造bean,是AOP實(shí)現(xiàn)的切入點(diǎn) - 記錄創(chuàng)建bean的ObjectFactory, 循環(huán)依賴的解決
- 屬性注入
- 初始化bean
a. 激活aware方法 -- 為實(shí)現(xiàn)Aware接口的的bean對象注入對應(yīng)的對象
b. 處理器的應(yīng)用 -- 為BeanFactory增加BeanPostProcessor,一種增強(qiáng)器,在執(zhí)行初始化方法之前和之后執(zhí)行,可以添加業(yè)務(wù)邏輯
c. 激活自定義的init方法 -- 配置文件的init-method配置 - 注冊DisposableBean
后記
bean的加載可以說是spring的兩大核心之一了,也是aop實(shí)現(xiàn)的切入點(diǎn),這部分代碼多多閱讀,可以把控spring的整體運(yùn)行原理,對于一些定制的需求也可以更靈活的實(shí)現(xiàn).
對于復(fù)雜的邏輯進(jìn)行分解,分成N個小函數(shù)的嵌套,每一層都是對下一層邏輯的總結(jié)和概要,這樣使得每一層邏輯會變得簡單容易理解.
附 測試代碼
- bean加載的各個情況
public class MyTestBean {
private String name = "testName";
private Integer id;
public MyTestBean(Integer id) {
this.id = id;
}
@Override
public String toString() {
return "MyTestBean{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
public MyTestBean() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private void init() {
System.out.println("測試bean被初始化了");
}
}
public abstract class LookupTestBean {
public void doSomething() {
System.out.println(getTestBean());
}
public abstract MyTestBean getTestBean();
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="myTestBean" class="com.pctf.beans.MyTestBean" init-method="init">
<property name="name" value="測試對象"></property>
<constructor-arg index="0" value="10"></constructor-arg>
</bean>
<bean name="lookupTestBean" class="com.pctf.beans.LookupTestBean">
<lookup-method name="getTestBean" bean="myTestBean"></lookup-method>
</bean>
<beans profile="dev">
</beans>
</beans>
public class AppTestLaunch {
public static void main(String[] args) {
XmlBeanFactory bf = new XmlBeanFactory( new ClassPathResource("spring-config.xml"));
bf.addBeanPostProcessor(new PostProcessorTest());
MyTestBean myTestBean = (MyTestBean) bf.getBean("myTestBean");
LookupTestBean lookupTestBean = (LookupTestBean) bf.getBean("lookupTestBean");
lookupTestBean.doSomething();
System.out.println(myTestBean.getName());
}
}
- 循環(huán)依賴的測試代碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.pctf.beans.TestBeanA" name="testBeanA">
<property name="name" value="測試BeanA"></property>
<!--<property name="testBeanB" ref="testBeanB"></property>-->
<constructor-arg value="testBeanB" index="0"></constructor-arg>
</bean>
<bean class="com.pctf.beans.TestBeanB" name="testBeanB">
<property name="name" value="測試BeanB"></property>
<!--<property name="testBeanA" ref="testBeanA"></property>-->
<constructor-arg value="testBeanA" index="0"></constructor-arg>
</bean>
</beans>
import com.pctf.beans.TestBeanA;
import com.pctf.beans.TestBeanB;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class BeanLoadingTest {
public static void main(String[] args) {
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("bean-loading-config.xml"));
//((XmlBeanFactory) bf).setAllowCircularReferences(false);
TestBeanA testBeanA = (TestBeanA) bf.getBean("testBeanA");
TestBeanB testBeanB = bf.getBean(TestBeanB.class);
System.out.println(testBeanA.getName());
System.out.println(testBeanB.getName());
}
}