Bean的加載概覽
1. 前言
在上一篇博客中,我們介紹到了IOC
容器初始化的過程中,會將BeanName
以及BeanDefinition
保存到IOC
的容器中,此時並沒有進行Bean
的加載,而是在通過調用getBean()
時纔會進行加載(如果沒有設置過lazyinit
屬性),而Bean
的加載是基於BeanDefiniton
進行加載的,那麼BeanDefiniton
是什麼?
通過名字可以知道,其是Bean
的定義信息,代碼如下:
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// 單例/原型的 Bean
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// Bean 代表的角色是
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// 設置/返回父 BeanDefinition 的名字
void setParentName(@Nullable String parentName);
String getParentName();
// 設置/返回 BeanClassName
void setBeanClassName(@Nullable String beanClassName);
String getBeanClassName();
// 設置/返回命名空間
void setScope(@Nullable String scope);
String getScope();
// 設置懶加載
void setLazyInit(boolean lazyInit);
// 是否是懶加載
boolean isLazyInit();
/**
* 設置該 Bean 依賴的BeanName。
* BeanFactory將優先初始化依賴的Bean
*/
void setDependsOn(@Nullable String... dependsOn);
// 返回依賴的 Bean的BeanName
String[] getDependsOn();
// 設置該 Bean 是否可以自動注入
void setAutowireCandidate(boolean autowireCandidate);
// 返回該 Bean 是否可以自動注入
boolean isAutowireCandidate();
// 設置該Bean是否是優先進行注入的Bean
void setPrimary(boolean primary);
// 返回該Bean是否是優先進行注入的Bean
boolean isPrimary();
// 設置 BeanFactory 的 Name
void setFactoryBeanName(@Nullable String factoryBeanName);
// 返回 BeanFactory 的 Name
String getFactoryBeanName();
// 設置工廠方法的名稱
void setFactoryMethodName(@Nullable String factoryMethodName);
// 返回工廠方法的名稱
String getFactoryMethodName();
// 返回該 Bean 構造函數的參數值
ConstructorArgumentValues getConstructorArgumentValues();
// 返回該 Bean 是否有構造函數的參數值
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
// 獲取該 Bean 要被注入的屬性值
MutablePropertyValues getPropertyValues();
//獲取該 Bean 是否有要被注入的屬性值
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
// 設置/返回初始化方法的名字
void setInitMethodName(@Nullable String initMethodName);
String getInitMethodName();
// 設置/返回 Bean 銷燬時方法的名字
void setDestroyMethodName(@Nullable String destroyMethodName);
String getDestroyMethodName();
// 設置/返回爲 Bean 設置的角色
void setRole(int role);
int getRole();
// 設置/返回爲 Bean 設置的描述
void setDescription(@Nullable String description);
String getDescription();
// 返回此bean定義的可解析類型,
ResolvableType getResolvableType();
// 是否是單例
boolean isSingleton();
// 是否是原型
boolean isPrototype();
// 是否是抽象類
boolean isAbstract();
// 返回此bean定義的資源的描述
String getResourceDescription();
// 返回 BeanDefinition
BeanDefinition getOriginatingBeanDefinition();
}
可以看到,xml
文件中定義的屬性都可以被映射到BeanDefinition
中,而我們正是由於BeanDefinition
可以按照我們在xml
文件中的意願進行初始化工作。
2. 簡介
Spring IOC
容器所起的作用如上圖所示,它會以某種方式加載 Configuration Metadata
,將其解析註冊到容器內部,然後會根據這些信息綁定整個系統的對象,最終組裝成一個可用的基於輕量級容器的應用系統。
Spring
在實現上述功能中,將整個流程分爲兩個階段:容器初始化階段和加載Bean
階段。
1、容器初始化階段:首先通過某種方式加載 Configuration Metadata
(主要是依據 Resource
、ResourceLoade
r 兩個體系),然後容器會對加載的 Configuration MetaData
進行解析和分析,並將分析的信息組裝成 BeanDefinition
,並將其保存註冊到相應的BeanDefinitionRegistry
中。至此,Spring IOC
的初始化工作完成。
2、加載 bean 階段:經過容器初始化階段後,應用程序中定義的 Bean
信息已經全部加載到系統中了,當我們顯示或者隱式地調用 getBean()
時,則會觸發加載Bean
階段。在這階段,容器會首先檢查所請求的對象是否已經初始化完成了,如果沒有,則會根據註冊的 Bean
信息實例化請求的對象,併爲其註冊依賴,然後將其返回給請求方。至此第二個階段也已經完成。
當我們顯示或者隱式地調用 getBean()
時,則會觸發加載 Bean
階段。如下:
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
內部調用 doGetBean()
name
:要獲取Bean
的名字requiredType
:要獲取Bean
的類型args
:創建Bean
時傳遞的參數。這個參數僅限於創建Bean
時使用typeCheckOnly
:是否爲類型檢查
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly)
throws BeansException {
// 獲取 beanName,這裏是一個轉換動作,將 name 轉換爲 beanName
final String beanName = transformedBeanName(name);
Object bean;
// 從緩存中或者實例工廠中獲取 bean
// *** 這裏會涉及到解決循環依賴 bean 的問題
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 因爲 Spring 只解決單例模式下得循環依賴,在原型模式下如果存在循環依賴則會拋出異常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 如果容器中沒有找到,則從父類容器中加載
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 {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
// 如果不是僅僅做類型檢查則是創建bean,這裏需要記錄
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 從容器中獲取 beanName 相應的 GenericBeanDefinition,並將其轉換爲
// RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 檢查給定的合併的 BeanDefinition
checkMergedBeanDefinition(mbd, beanName, args);
// 處理所依賴的 bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 若給定的依賴 bean 已經註冊爲依賴給定的bean
// 循環依賴的情況
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 緩存依賴調用
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// bean 實例化
// 單例模式
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// 顯示從單利緩存中刪除 bean 實例
// 因爲單例模式下爲了解決循環依賴,可能他已經存在了,所以銷燬它
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 原型模式
else if (mbd.isPrototype()) {
// 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 {
// 從指定的 scope 下創建 bean
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 {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
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) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
3. 獲取 beanName
final String beanName = transformedBeanName(name);
這裏傳遞的是name
,不一定就是 beanName
(即id
),可能是 aliasName
,也有可能是 FactoryBean
,所以這裏需要調用 transformedBeanName()
方法對 name
進行一番轉換,主要如下:
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
// 去除 FactoryBean 的修飾符
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
// 如果 beanName 是以 & 開頭,則說明是 FactoryBean
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
// 去除 &
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}
// 轉換 aliasName
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
// 返回別名對應的 BeanName
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
主要處理過程包括兩步:
- 去除
FactoryBean
的修飾符。如果 name 以 “&” 爲前綴,那麼會去掉該 “&”,例如,name = "&studentService"
,則會是name = "studentService"
。 - 取指定的
alias
所表示的最終beanName
。主要是一個循環獲取beanName
的過程,例如別名A
指向名稱爲B
的bean
則返回B
,若別名A
指向別名B
,別名B
指向名稱爲C
的bean
,則返回C
。
4. 從單例 bean 緩存中獲取 bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從 map 緩存中獲取 BeanName 對應的單實例Bean
Object singletonObject = this.singletonObjects.get(beanName);
// 如果獲取不到實例,並且當前不在創建該實例
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 從 earlySingletonObjects 獲取
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果 earlySingletonObjects 獲取不到,則從單例的工廠中去獲取
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 保存到 earlySingletonObjects 中
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
我們知道單例模式的 bean
在整個過程中只會被創建一次,第一次創建後會將該 bean
加載到緩存中,後面在獲取 bean
就會直接從單例緩存中獲取。
如果從緩存中得到了 bean
,則需要調用 getObjectForBeanInstance()
對 bean
進行實例化處理,因爲緩存中記錄的是最原始的 bean
狀態,我們得到的不一定是我們最終想要的 bean
。
5. 原型模式依賴檢查與 parentBeanFactory
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
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 {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
Spring 只處理單例模式下得循環依賴,對於原型模式的循環依賴直接拋出異常。
主要原因還是在於Spring
解決循環依賴的策略有關。對於單例模式Spring
在創建 bean
的時候並不是等 bean
完全創建完成後纔會將 bean
添加至緩存中,而是不等bean
創建完成就會將創建bean
的 ObjectFactory
提早加入到緩存中,這樣一旦下一個 bean
創建的時候需要依賴 bean
時則直接使用 ObjectFactroy
。
但是原型模式我們知道是沒法使用緩存的,所以 Spring
對原型模式的循環依賴處理策略則是不處理。
如果容器緩存中沒有相對應的 BeanDefinition
則會嘗試從父類工廠(parentBeanFactory
)中加載,然後再去遞歸調用 getBean()
。
5.1 依賴處理
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);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
每個 Bean
都不是單獨工作的,它會依賴其他 Bean
,其他 Bean
也會依賴它,對於依賴的 Bean
,它會優先加載,所以在 Spring 的加載順序中,在初始化某一個 bean 的時候首先會初始化這個 bean 的依賴。
5.2 作用域處理
Spring bean
的作用域默認爲 singleton
,當然還有其他作用域,如prototype
、request
、session
等,不同的作用域會有不同的初始化策略。
在調用方法時,有一個 requiredType
參數,該參數的功能就是將返回的 bean
轉換爲 requiredType
類型。當然就一般而言我們是不需要進行類型轉換的,也就是requiredType
爲空(比如 ),但有可能會存在這種情況,比如我們返回的 bean
類型爲 String
,我們在使用的時候需要將其轉換爲 Integer
,那麼這個時候 requiredType
就有用武之地了。當然我們一般是不需要這樣做的。