1. 程式人生 > 實用技巧 >Spring IoC bean 的載入

Spring IoC bean 的載入

前言

本系列全部基於 Spring 5.2.2.BUILD-SNAPSHOT 版本。因為 Spring 整個體系太過於龐大,所以只會進行關鍵部分的原始碼解析。

本篇文章主要介紹 Spring IoC 容器是怎麼載入 bean 的。

正文

我們先看一下Spring IoC BeanDefinition 的載入和註冊一文中獲取 bean 的例項程式碼:

public class BeanDefinitionDemo {

    public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("META-INF/bean-definition.xml");
User user = beanFactory.getBean("user", User.class);
System.err.println(user);
} }

通過 beanFactory.getBean()

這個方法就獲取了在 XML 中定義的 bean,下面我們就重點分析這個方法背後做了什麼操作。

在正式開始之前,我們先了解一下 FactoryBean 及其用法。

FactoryBean 介紹

FactoryBean 介面對於 Spring 框架來說佔有重要的地位,Spring 自身就提供了70多個 FactoryBean 的實現。它們隱藏了一下複雜 bean 的細節,給上層應用帶來了便利。下面是該介面的定義:

public interface FactoryBean<T> {

    // 返回由FactoryBean建立的bean例項,如果isSingleton()返回true,
// 則該例項會放到Spring容器中單例快取池中
@Nullable
T getObject() throws Exception; // 返回FactoryBean建立的bean型別
@Nullable
Class<?> getObjectType(); // 返回由FactoryBean建立的bean例項的作用域是singleton還是prototype
default boolean isSingleton() {
return true;
} }

當配置檔案中 <bean>class 屬性配置的實現類時 FactoryBean 時,通過 getBean() 返回的不是 FactoryBean 本身,而是 FactoryBean#getObject() 所返回的物件,相當於 FactoryBean#getObject() 代理了 getBean()。下面用簡單的程式碼演示一下:

首先定義一個 Car 實體類:

public class Car {

    private Integer maxSpeed;
private String brand;
private Double price; public Integer getMaxSpeed() {
return maxSpeed;
} public void setMaxSpeed(Integer maxSpeed) {
this.maxSpeed = maxSpeed;
} public String getBrand() {
return brand;
} public void setBrand(String brand) {
this.brand = brand;
} public Double getPrice() {
return price;
} public void setPrice(Double price) {
this.price = price;
}
}

上面的實體類,如果用傳統方式配置,每一個屬性都會對應一個 <property> 元素標籤。如果用 FactoryBean 的方式實現就會靈活一點,下面通過逗號分隔的方式一次性的為 Car 的所有屬性配置值。

public class CarFactoryBean implements FactoryBean<Car> {

    private String carInfo;

    @Override
public Car getObject() throws Exception {
Car car = new Car();
String[] infos = carInfo.split(",");
car.setBrand(infos[0]);
car.setMaxSpeed(Integer.valueOf(infos[1]));
car.setPrice(Double.valueOf(infos[2]));
return car;
} @Override
public Class<?> getObjectType() {
return Car.class;
} @Override
public boolean isSingleton() {
return true;
} public String getCarInfo() {
return carInfo;
} public void setCarInfo(String carInfo) {
this.carInfo = carInfo;
}
}

接下來,我們在 XML 中配置。

<bean id="car" class="com.leisurexi.ioc.domain.CarFactoryBean">
<property name="carInfo" value="超級跑車,400,2000000"/>
</bean>

最後看下測試程式碼和執行結果:

@Test
public void factoryBeanTest() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("META-INF/factory-bean.xml");
Car car = beanFactory.getBean("car", Car.class);
System.out.println(car);
CarFactoryBean carFactoryBean = beanFactory.getBean("&car", CarFactoryBean.class);
System.out.println(carFactoryBean);
}

可以看到如果 beanName 前面加上 & 獲取的是 FactoryBean 本身,不加獲取的 getObject() 返回的物件。

FactoryBean 的特殊之處在於它可以向容器中註冊兩個 bean,一個是它本身,一個是 FactoryBean.getObject() 方法返回值所代表的 bean

bean 的載入

AbstractBeanFactory#getBean

public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
// 呼叫doGetBean方法(方法以do開頭實際做操作的方法)
return doGetBean(name, requiredType, null, false);
} /**
* @param name bean的名稱
* @param requiredType bean的型別
* @param args 顯示傳入的構造引數
* @param typeCheckOnly 是否僅僅做型別檢查
*/
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; // 直接嘗試從快取獲取或 singletonFactories 中的 ObjectFactory 中獲取,見下文詳解
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 檢查bean是否是FactoryBean的實現。不是直接返回bean,
// 是的話首先檢查beanName是否以&開頭,如果是返回FactoryBean本身,
// 不是呼叫FactoryBean#getObject()返回物件,見下文詳解
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// 只有在單例情況下才會去嘗試解決迴圈依賴,原型模式下,如果存在A中有
// B屬性,B中有A屬性,那麼當依賴注入時,就會產生當A還未建立完的時候
// 對於B的建立而在此返回建立A,造成迴圈依賴
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
} // 檢查當前bean的BeanDefinition是否在當前的bean工廠中,
// 不在遞迴呼叫父工廠的getBean()去獲取bean
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// 如果不是僅僅做型別檢查,則是建立bean,這裡要進行記錄
if (!typeCheckOnly) {
// 記錄bean已經建立過
markBeanAsCreated(beanName);
} try {
// 合併BeanDefinition,見下文詳解
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args); // 例項化bean前先例項化依賴bean,也就是depends-on屬性中配置的beanName
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 檢查是否迴圈依賴,即當前bean依賴dep,dep依賴當前bean,見下文詳解
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 將dep和beanName的依賴關係放入到快取中,見下文詳解
registerDependentBean(dep, beanName);
try {
// 獲取依賴dep對應的bean例項,如果還未建立例項,則先去建立
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
} // 如果 bean 的作用域是單例
if (mbd.isSingleton()) {
// 建立和註冊單例 bean,見下文詳解
sharedInstance = getSingleton(beanName, () -> {
try {
// 建立 bean 例項
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
// 獲取bean例項
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// bean 的作用域是原型
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
// 原型 bean 建立前回調,
// 預設實現是將 beanName 儲存到 prototypesCurrentlyInCreation 快取中
beforePrototypeCreation(beanName);
// 建立 bean 例項
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 原型 bean 建立後回撥,
// 預設實現是將 beanName 從prototypesCurrentlyInCreation 快取中移除
afterPrototypeCreation(beanName);
}
// 獲取bean例項
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 + "'");
}
try {
// 其他 Scope 的 bean 建立
// 新建了一個 ObjectFactory,並且重寫了 getObject 方法
Object scopedInstance = scope.get(beanName, () -> {
// 呼叫原型 bean 建立前回調
beforePrototypeCreation(beanName);
try {
// 建立 bean 例項,下篇文章詳解
return createBean(beanName, mbd, args);
}
finally {
// 呼叫原型 bean 建立後回撥
afterPrototypeCreation(beanName);
}
});
// 獲取bean例項
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
} // 檢查所需的型別是否與實際 bean 例項的型別匹配
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
// 如果型別不等,進行轉換,轉換失敗丟擲異常;轉換成功直接返回
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
// 返回 bean 例項
return (T) bean;
}

上面方法就是獲取 bean 的整個流程,下面我們對其呼叫的其它主要方法來一一分析。

轉換對應的 beanName

AbstractBeanFactory#transformedBeanName

protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
} // BeanFactoryUtils.java
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
// 如果name不是&開頭,直接返回
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
// 去除name的&字首
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
} // SimpleAliasRegistry.java
public String canonicalName(String name) {
String canonicalName = name;
String resolvedName;
// 如果name是別名,則會迴圈去查詢bean的實際名稱
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}

嘗試從單例快取獲取 bean

AbstractBeanFactory#getSingleton

public Object getSingleton(String beanName) {
// allowEarlyReference設定為true表示允許早期依賴
return getSingleton(beanName, true);
} protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 先從一級快取中,檢查單例快取是否存在
Object singletonObject = this.singletonObjects.get(beanName);
// 如果為空,並且當前bean正在建立中,鎖定全域性變數進行處理
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 從二級快取中獲取
singletonObject = this.earlySingletonObjects.get(beanName);
// 二級快取為空 && bean允許提前曝光
if (singletonObject == null && allowEarlyReference) {
// 從三級快取中獲取bean對應的ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 呼叫預先設定的getObject(),獲取bean例項
singletonObject = singletonFactory.getObject();
// 放入到二級快取中,並從三級快取中刪除
// 這時bean已經例項化完但還未初始化完
// 在該bean未初始化完時如果有別的bean引用該bean,可以直接從二級快取中取出返回
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

上面方法主要就是嘗試從快取中獲取 bean,快取有三級,這也是 Spring 解決迴圈依賴的關鍵所在;後續會在 迴圈依賴 中重點講述。

獲取 bean 例項物件

AbstractBeanFactory#getObjectForBeanInstance

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // name 是否以 & 開頭
if (BeanFactoryUtils.isFactoryDereference(name)) {
// 如果是 null 直接返回
if (beanInstance instanceof NullBean) {
return beanInstance;
}
// beanName 以 & 開頭,但又不是 FactoryBean 型別,丟擲異常
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
// 設定 isFactoryBean 為 true
if (mbd != null) {
mbd.isFactoryBean = true;
}
// 返回 bean 例項
return beanInstance;
} // name 不是 & 開頭,並且不是 FactoryBean 型別,直接返回
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
} // 到這裡就代表name不是&開頭,且是FactoryBean型別
// 即獲取FactoryBean.getObject()方法返回值所代表的bean
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
// 從快取中獲取例項
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// 將 beanInstance 強轉成 FactoryBean
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// 合併 BeanDefinition
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 獲取例項
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
} // FactoryBeanRegistrySupport.java
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// 如果是單例 bean,並且已經存在快取中
if (factory.isSingleton() && containsSingleton(beanName)) {
// 加鎖
synchronized (getSingletonMutex()) {
// 從快取中獲取
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 呼叫 FactoryBean 的 getObject() 獲取例項
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
// 如果該 beanName 已經在快取中存在,則將 object 替換成快取中的
if (alreadyThere != null) {
object = alreadyThere;
}
else {
if (shouldPostProcess) {
// 如果當前 bean 還在建立中,直接返回
if (isSingletonCurrentlyInCreation(beanName)) {
return object;
}
// 單例 bean 建立前回調
beforeSingletonCreation(beanName);
try {
// 對從 FactoryBean 獲得給定物件的後置處理,預設按原樣返回
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
// 單例 bean 建立後回撥
afterSingletonCreation(beanName);
}
}
if (containsSingleton(beanName)) {
// 將 beanName 和 object 放到 factoryBeanObjectCache 快取中
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
// 返回例項
return object;
}
}
else {
// 呼叫 FactoryBean 的 getObject() 獲取例項
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
// 對從 FactoryBean 獲得給定物件的後置處理,預設按原樣返回
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
// 返回例項
return object;
}
} // FactoryBeanRegistrySupport.java
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName) throws BeanCreationException { Object object;
try {
// 呼叫 getObject() 獲取例項
object = factory.getObject();
}
// 省略異常處理... // 如果 object 為 null,並且當前 singleton bean 正在建立中,丟擲異常
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName, "FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
// 返回 object 例項
return object;
}

上面程式碼總結起來就是:如果 beanName& 開頭,直接返回 FactoryBean 例項;否則呼叫 getObject() 方法獲取例項,然後執行 postProcessObjectFromFactoryBean() 回撥,可以在回撥方法中修改例項,預設按原樣返回。

合併 bean 定義元資訊

AbstractBeanFactory#getMergedLocalBeanDefinition

下文將合併後的 BeanDefinition 簡稱為 MergedBeanDefinition

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// 從快取獲取MergedBeanDefinition
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
// 如果存在MergedBeanDefinition,並且不是過期的,直接返回
if (mbd != null && !mbd.stale) {
return mbd;
}
// 獲取已經註冊的BeanDefinition然後去合併
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
} protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
throws BeanDefinitionStoreException {
// 頂級bean獲取合併後的BeanDefinition
return getMergedBeanDefinition(beanName, bd, null);
} /**
* @param containingBd 如果是巢狀bean該值為頂級bean,如果是頂級bean該值為null
*/
protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
throws BeanDefinitionStoreException {
// 加鎖
synchronized (this.mergedBeanDefinitions) {
// 本次的RootBeanDefinition
RootBeanDefinition mbd = null;
// 以前的RootBeanDefinition
RootBeanDefinition previous = null; // 如果bean是頂級bean,直接獲取MergedBeanDefinition
if (containingBd == null) {
mbd = this.mergedBeanDefinitions.get(beanName);
}
// 沒有MergedBeanDefinition || BeanDefinition過期了
if (mbd == null || mbd.stale) {
previous = mbd;
// 如果bean沒有parent
if (bd.getParentName() == null) {
// 如果bd本身就是RootBeanDefinition直接複製一份,否則建立一個
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
mbd = new RootBeanDefinition(bd);
}
}
else {
// bean有parent
BeanDefinition pbd;
try {
// 獲取parent bean的實際名稱
String parentBeanName = transformedBeanName(bd.getParentName());
if (!beanName.equals(parentBeanName)) {
// 當前beanName不等於它的parentBeanName
// 獲取parent的MergedBeanDefinition
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
// 如果parentBeanName與bd的beanName相同,則拿到父BeanFactory
// 只有在存在父BeanFactory的情況下,才允許parentBeanName與自己相同
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
// 如果父BeanFactory是ConfigurableBeanFactory型別
// 則通過父BeanFactory獲取parent的MergedBeanDefinition
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {
// 如果父BeanFactory不是ConfigurableBeanFactory,丟擲異常
throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent");
}
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
}
// 使用父MergedBeanDefinition構建一個新的RootBeanDefinition物件(深拷貝)
mbd = new RootBeanDefinition(pbd);
// 覆蓋與parent相同的屬性
mbd.overrideFrom(bd);
} // 如果bean沒有設定scope屬性,預設是singleton
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
} // 當前bean是巢狀bean && 頂級bean的作用域不是單例 && 當前bean的作用域是單例
// 這裡總結起來就是,如果頂層bean不是單例的,那麼巢狀bean也不能是單例的
if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
// 設定當前bean的作用域和頂級bean一樣
mbd.setScope(containingBd.getScope());
} // 當前bean是頂級bean && 快取bean的元資料(該值預設為true)
if (containingBd == null && isCacheBeanMetadata()) {
// 將當前bean的MergedBeanDefinition快取起來
this.mergedBeanDefinitions.put(beanName, mbd);
}
}
// 以前的RootBeanDefinition不為空,拷貝相關的BeanDefinition快取
if (previous != null) {
copyRelevantMergedBeanDefinitionCaches(previous, mbd);
}
return mbd;
}
}

上面程式碼主要是獲取 MergedBeanDefinition ,主要步驟如下:

  1. 首先從快取中獲取 beanMergedBeanDefinition,如果存在並且未過期直接返回。

  2. 不存在或者已過期的 MergedBeanDefinition ,獲取已經註冊的 BeanDefinition 去作為頂級 bean 合併。

  3. bean 沒有 parent (就是 XML 中的 parent 屬性),直接封裝成 RootBeanDefinition

  4. beanparent ,先去獲取父 MergedBeanDefinition ,然後覆蓋和合並與 parent 相同的屬性。

    注意:這裡只有 abstractscopelazyInitautowireModedependencyCheckdependsOnfactoryBeanNamefactoryMethodNameinitMethodNamedestroyMethodName 會覆蓋,而 constructorArgumentValuespropertyValuesmethodOverrides 會合並。

  5. 如果沒有設定作用域,預設作用域為 singleton

  6. 快取 MergedBeanDefinition

上文中提到如果 beanparent,會合並一些屬性,這裡我們稍微展示一下合併後的 propertyValues:

首先定義一個 SuperUser 繼承上面定義的 User,如下:

public class SuperUser extends User {

    private String address;

    public String getAddress() {
return address;
} public void setAddress(String address) {
this.address = address;
} @Override
public String toString() {
return "SuperUser{" +
"address='" + address + '\'' +
'}';
} }

然後我們在 XML 檔案中配置一下,如下:

<bean id="superUser" class="com.leisurexi.ioc.domain.SuperUser" parent="user">
<property name="address" value="北京"/>
</bean>

然後下圖是我 Debug 的截圖,可以看到 superUserpropertyValues 合併了 useridname 屬性。

上文還提到了巢狀 bean ,下面我們簡單看一下什麼是巢狀 bean

在 Spring 中,如果某個 bean 所依賴的 bean 不想被 Spring 容器直接訪問,可以使用巢狀 bean。和普通的 bean 一樣,使用 bean 元素來定義巢狀的 bean,巢狀 bean 只對它的外部 bean 有效,Spring 無法直接訪問巢狀 bean ,因此定義巢狀 bean 也無需指定 id 屬性。如下配置片段是一個巢狀 bean 示例:

採用上面的配置形式可以保證巢狀 bean 不能被容器訪問,因此不用擔心其他程式修改巢狀 bean。外部 bean 的用法和使用結果和以前沒有區別。

巢狀 bean 提高了 bean 的內聚性,但是降低了程式的靈活性。只有在確定無需通過 Spring 容器訪問某個 bean 例項時,才考慮使用巢狀 bean 來定義。

尋找依賴

DefaultSingletonBeanRegistry#isDependent

protected boolean isDependent(String beanName, String dependentBeanName) {
// 加鎖
synchronized (this.dependentBeanMap) {
// 檢測beanName和dependentBeanName是否有迴圈依賴
return isDependent(beanName, dependentBeanName, null);
}
} private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
// 如果當前bean已經檢測過,直接返回false
if (alreadySeen != null && alreadySeen.contains(beanName)) {
return false;
}
// 解析別名,獲取實際的beanName
String canonicalName = canonicalName(beanName);
// 獲取canonicalName所依賴beanName集合
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
// 如果為空,兩者還未確定依賴關係,返回false
if (dependentBeans == null) {
return false;
}
// 如果dependentBeanName已經存在於快取中,兩者已經確定依賴關係,返回true
if (dependentBeans.contains(dependentBeanName)) {
return true;
}
// 迴圈檢查,即檢查依賴canonicalName的所有beanName是否被dependentBeanName依賴(即隔層依賴)
for (String transitiveDependency : dependentBeans) {
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
// 將已經檢查過的記錄下來,下次直接跳過
alreadySeen.add(beanName);
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
return true;
}
}
return false;
}

DefaultSingletonBeanRegistry#registerDependentBean

public void registerDependentBean(String beanName, String dependentBeanName) {
// 解析別名,獲取實際的beanName
String canonicalName = canonicalName(beanName);
// 加鎖
synchronized (this.dependentBeanMap) {
// 獲取canonicalName依賴beanName集合,如果為空預設建立一個LinkedHashSet當做預設值
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
// 如果dependentBeanName已經記錄過了,直接返回
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
// 加鎖
synchronized (this.dependenciesForBeanMap) {
// 這裡是和上面的dependentBeanMap倒過來,key為dependentBeanName
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}

下面我們舉個A、B的 depends-on 屬性都是對方的例子:

首先獲取A,呼叫 isDependent() 方法,因為第一次獲取A,所以 dependentBeanMap 中沒有記錄依賴關係,直接返回 false;接著呼叫registerDependentBean(),這裡會向 dependentBeanMap 中反過來儲存依賴關係,也就是以B為 key value 是一個包含A的 Set集合。

接著會呼叫 getBean() 方法獲取B,首先呼叫 isDependent() 方法,因為在獲取A時已經儲存了B的依賴關係,所以獲取到的dependentBeans 的集合中包含A,所以直接返回true,丟擲迴圈引用異常。

這個方法又引入了一個跟 dependentBeanMap 類似的快取 dependenciesForBeanMap。這兩個快取很容易搞混,這裡再舉一個簡單的例子:A 依賴 B,那麼 dependentBeanMap 存放的是 key 為 B,value 為含有 A 的 Set;而 dependenciesForBeanMap 存放的是key 為 A,value 為含有 B 的 Set

建立和註冊單例 bean

DefaultSingletonBeanRegistry#getSingleton

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 加鎖
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
// 快取中不存在當前 bean,也就是當前 bean 第一次建立
if (singletonObject == null) {
// 如果當前正在銷燬 singletons,丟擲異常
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
// 建立單例 bean 之前的回撥
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 獲取 bean 例項,在此處才會去真正呼叫建立 bean 的方法
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// 省略異常處理...
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 建立單例 bean 之後的回撥
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 將 singletonObject 放入快取
addSingleton(beanName, singletonObject);
}
}
// 返回 bean 例項
return singletonObject;
}
} // 單例 bean 建立前的回撥方法,預設實現是將 beanName 加入到當前正在建立 bean 的快取中,
// 這樣便可以對迴圈依賴進行檢測
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
} // 單例 bean 建立後的回撥方法,預設實現是將 beanName 從當前正在建立 bean 的快取中移除
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
} protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 這邊bean已經初始化完成了,放入一級快取
this.singletonObjects.put(beanName, singletonObject);
// 移除三級快取
this.singletonFactories.remove(beanName);
// 移除二級快取
this.earlySingletonObjects.remove(beanName);
// 將 beanName 新增到已註冊 bean 快取中
this.registeredSingletons.add(beanName);
}
}

自定義作用域示例

我們實現一個 ThreadLocal 級別的作用域,也就是同一個執行緒內 bean 是同一個例項,不同執行緒的 bean 是不同例項。首先我們繼承 Scope 介面實現,其中方法。如下:

public class ThreadLocalScope implements Scope {

    /** scope 名稱,在 XML 中的 scope 屬性就配置此名稱 */
public static final String SCOPE_NAME = "thread-local"; private final NamedThreadLocal<Map<String, Object>> threadLocal = new NamedThreadLocal<>("thread-local-scope"); /**
* 返回例項物件,該方法被 Spring 呼叫
*/
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> context = getContext();
Object object = context.get(name);
if (object == null) {
object = objectFactory.getObject();
context.put(name, object);
}
return object;
} /**
* 獲取上下文 map
*/
@NonNull
private Map<String, Object> getContext() {
Map<String, Object> map = threadLocal.get();
if (map == null) {
map = new HashMap<>();
threadLocal.set(map);
}
return map;
} @Override
public Object remove(String name) {
return getContext().remove(name);
} @Override
public void registerDestructionCallback(String name, Runnable callback) {
// TODO
} @Override
public Object resolveContextualObject(String key) {
Map<String, Object> context = getContext();
return context.get(key);
} @Override
public String getConversationId() {
return String.valueOf(Thread.currentThread().getId());
} }

上面的 ThreadLocalScope 重點關注下 get() 即可,該方法是被 Spring 呼叫的。

然後在 XML 中配置 beanscopethread-local。如下:

<bean id="user" name="user" class="com.leisurexi.ioc.domain.User" scope="thread-local">
<property name="id" value="1"/>
<property name="name" value="leisurexi"/>
</bean>

接著我們測試一下。測試類:

@Test
public void test() throws InterruptedException {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 註冊自定義作用域
beanFactory.registerScope(ThreadLocalScope.SCOPE_NAME, new ThreadLocalScope());
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("META-INF/custom-bean-scope.xml");
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(() -> {
User user = beanFactory.getBean("user", User.class);
System.err.printf("[Thread id :%d] user = %s%n", Thread.currentThread().getId(), user.getClass().getName() + "@" + Integer.toHexString(user.hashCode()));
User user1 = beanFactory.getBean("user", User.class);
System.err.printf("[Thread id :%d] user1 = %s%n", Thread.currentThread().getId(), user1.getClass().getName() + "@" + Integer.toHexString(user1.hashCode()));
});
thread.start();
thread.join();
}
}

說一下我們這裡的主要思路,新建了三個執行緒,查詢執行緒內 user bean 是否相等,不同執行緒是否不等。

結果如下圖:

總結

本文主要介紹了 getBean() 方法流程,我們可以重新梳理一下思路:

  1. 獲取 bean 實際名稱,如果快取中存在直接取出實際 bean 返回。
  2. 快取中不存在,判斷當前工廠是否有 BeanDefinition ,沒有遞迴去父工廠建立 bean
  3. 合併 BeanDefinition ,如果 depends-on 不為空,先去初始化依賴的 bean
  4. 如果 bean 的作用域是單例,呼叫 createBean() 方法建立例項,這個方法會執行 bean 的其它生命週期回撥,以及屬性賦值等操作;接著執行單例 bean 建立前後的生命週期回撥方法,並放入 singletonObjects 快取起來。
  5. 如果 bean 的作用域是原型,呼叫 createBean() 方法建立例項,並執行原型 bean 前後呼叫生命週期回撥方法。
  6. 如果 bean 的作用域是自定義的,獲取對應的 Scope 物件,呼叫重寫的 get() 方法獲取例項,並執行原型 bean 前後呼叫生命週期回撥方法。
  7. 最後檢查所需的型別是否與實際 bean 例項的型別匹配,如果不等進行轉換,最後返回例項。

關於 createBean() 方法的細節,會在後續文章中進行分析。

最後,我模仿 Spring 寫了一個精簡版,程式碼會持續更新。地址:https://github.com/leisurexi/tiny-spring

參考