前言
本系列全部基於 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
,主要步驟如下:
-
首先從緩存中獲取 bean
的 MergedBeanDefinition
,如果存在並且未過期直接返回。
-
不存在或者已過期的 MergedBeanDefinition
,獲取已經註冊的 BeanDefinition
去作為頂級 bean
合併。
-
bean
沒有 parent
(就是 XML 中的 parent 屬性),直接封裝成 RootBeanDefinition
。
-
bean
有 parent
,先去獲取父 MergedBeanDefinition
,然後覆蓋和合併與 parent
相同的屬性。
注意:這裏只有 abstract
、scope
、lazyInit
、autowireMode
、dependencyCheck
、dependsOn
、factoryBeanName
、factoryMethodName
、initMethodName
、destroyMethodName
會覆蓋,而 constructorArgumentValues
、propertyValues
、methodOverrides
會合併。
-
如果沒有設置作用域,默認作用域為 singleton
。
-
緩存 MergedBeanDefinition
。
上文中提到如果 bean
有 parent
,會合併一些屬性,這裏我們稍微展示一下合併后的 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 的截圖,可以看到 superUser
的 propertyValues
合併了 user
的 id
和 name
屬性。
上文還提到了嵌套 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 中配置 bean
的 scope
為 thread-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()
方法流程,我們可以重新梳理一下思路:
- 獲取
bean
實際名稱,如果緩存中存在直接取出實際 bean
返回。
- 緩存中不存在,判斷當前工廠是否有
BeanDefinition
,沒有遞歸去父工廠創建 bean
。
- 合併
BeanDefinition
,如果 depends-on
不為空,先去初始化依賴的 bean
。
- 如果
bean
的作用域是單例,調用 createBean()
方法創建實例,這個方法會執行 bean
的其它生命周期回調,以及屬性賦值等操作;接着執行單例 bean
創建前後的生命周期回調方法,並放入 singletonObjects
緩存起來。
- 如果
bean
的作用域是原型,調用 createBean()
方法創建實例,並執行原型 bean
前後調用生命周期回調方法。
- 如果
bean
的作用域是自定義的,獲取對應的 Scope
對象,調用重寫的 get()
方法獲取實例,並執行原型 bean
前後調用生命周期回調方法。
- 最後檢查所需的類型是否與實際
bean
實例的類型匹配,如果不等進行轉換,最後返回實例。
關於 createBean()
方法的細節,會在後續文章中進行分析。
最後,我模仿 Spring 寫了一個精簡版,代碼會持續更新。地址:https://github.com/leisurexi/tiny-spring。
參考
- 《Spring 源碼深度解析》—— 郝佳
- https://github.com/geektime-geekbang/geekbang-lessons
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※南投搬家公司費用需注意的眉眉角角,別等搬了再說!
※新北清潔公司,居家、辦公、裝潢細清專業服務