Spring(四)核心容器 - BeanDefinition 解析
前言
在上篇文章中,我們討論了 refresh 的前四個方法,主要是對 ApplicationContext 上下文啟動做一些準備工作。原計劃是對接下來的 invokeBeanFactoryPostProcessors 方法進行討論,但該方法涉及 Spring 中一個非常重要的概念: BeanDefinition,所以,這裡先對 BeanDefinition 進行討論,這樣也有利於完善 Spring 的知識體系。
注:本篇文章使用的 SpringBoot 版本為 2.0.3.RELEASE,其 Spring 版本為 5.0.7.RELEASE
正文
現如今,我們一般獲取物件的方式有兩種,一種是手動直接 new;另一種是交給 Spring 管理,Spring 將管理的物件稱之為 Bean,容器會先例項化 Bean,然後自動注入,例項化的過程就需要依賴 BeanDefinition。
BeanDefinition 用於儲存 Bean 的相關資訊,包括屬性、構造方法引數、依賴的 Bean 名稱及是否單例、延遲載入等,它是例項化 Bean 的原材料,Spring 就是根據 BeanDefinition 中的資訊例項化 Bean。
BeanDefinition的繼承體系
BeanDefinition 是一個介面,它有多個實現類,這些實現類分別描述不同型別的 Bean。
BeanDefinition
一個 BeanDefinition 描述了一個 Bean 例項,例項包含屬性值、構造方法引數值以及更多實現資訊。該 BeanDefinition 只是是一個最小的介面,主要目的是允許修改屬性值和其他 Bean 元資料,這裡列出幾個核心方法。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { // 單例、原型識別符號 String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; // 標識 Bean 的類別,分別對應 使用者定義的 Bean、來源於配置檔案的 Bean、Spring 內部的 Bean int ROLE_APPLICATION = 0; int ROLE_SUPPORT = 1; int ROLE_INFRASTRUCTURE = 2; // 設定、返回 Bean 的父類名稱 void setParentName(@Nullable String parentName); String getParentName(); // 設定、返回 Bean 的 className void setBeanClassName(@Nullable String beanClassName); String getBeanClassName(); // 設定、返回 Bean 的作用域 void setScope(@Nullable String scope); String getScope(); // 設定、返回 Bean 是否懶載入 void setLazyInit(boolean lazyInit); boolean isLazyInit(); // 設定、返回當前 Bean 所依賴的其它 Bean 名稱。 void setDependsOn(@Nullable String... dependsOn); String[] getDependsOn(); // 設定、返回 Bean 是否可以自動注入。只對 @Autowired 註解有效 void setAutowireCandidate(boolean autowireCandidate); boolean isAutowireCandidate(); // 設定、返回當前 Bean 是否為主要候選 Bean 。 // 當同一個介面有多個實現類時,通過該屬性來配置某個 Bean 為主候選 Bean。 void setPrimary(boolean primary); boolean isPrimary(); // 設定、返回建立該 Bean 的工廠類。 void setFactoryBeanName(@Nullable String factoryBeanName); String getFactoryBeanName(); // 設定、返回建立該 Bean 的工廠方法 void setFactoryMethodName(@Nullable String factoryMethodName); String getFactoryMethodName(); // 返回該 Bean 構造方法引數值、所有屬性 ConstructorArgumentValues getConstructorArgumentValues(); MutablePropertyValues getPropertyValues(); // 返回該 Bean 是否是單例、是否是非單例、是否是抽象的 boolean isSingleton(); boolean isPrototype(); boolean isAbstract(); // 返回 Bean 的類別。類別對應上面的三個屬性值。 int getRole(); ... }
可以看到 BeanDefinition 介面提供了一系列操作 Bean 元資料的set、get方法,這些操作為 Bean 的描述定義了一套模板,具體的實現則交由子類。
AnnotatedBeanDefinition
AnnotatedBeanDefinition 是 BeanDefinition 子介面之一,該介面擴充套件了 BeanDefinition 的功能,其用來操作註解元資料。一般情況下,通過註解方式得到的 Bean(@Component、@Bean),其 BeanDefinition 型別都是該介面的實現類。
public interface AnnotatedBeanDefinition extends BeanDefinition {
// 獲得當前 Bean 的註解元資料
AnnotationMetadata getMetadata();
// 獲得當前 Bean 的工廠方法上的元資料
MethodMetadata getFactoryMethodMetadata();
}
該介面可以返回兩個元資料的類:
AnnotationMetadata:主要對 Bean 的註解資訊進行操作,如:獲取當前 Bean 標註的所有註解、判斷是否包含指定註解。
MethodMetadata:方法的元資料類。提供獲取方法名稱、此方法所屬類的全類名、是否是抽象方法、判斷是否是靜態方法、判斷是否是final方法等。
AbstractBeanDefinition
AbstractBeanDefinition 是 BeanDefinition 的子抽象類,也是其他 BeanDefinition 型別的基類,其實現了介面中定義的一系列操作方法,並定義了一系列的常量屬性,這些常量會直接影響到 Spring 例項化 Bean 時的策略。核心屬性如下。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
// 預設的 SCOPE,預設是單例
public static final String SCOPE_DEFAULT = "";
// 不進行自動裝配
public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
// 根據 Bean 的名字進行自動裝配,byName
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
// 根據 Bean 的型別進行自動裝配,byType
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
// 根據構造器進行自動裝配
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
// 首先嚐試按構造器自動裝配。如果失敗,再嘗試使用 byType 進行自動裝配。(Spring 3.0 之後已廢除)
public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;
// 通過依賴檢查來檢視 Bean 的每個屬性是否都設定完成
// 以下常量分別對應:不檢查、對依賴物件檢查、對基本型別,字串和集合進行檢查、對全部屬性進行檢查
public static final int DEPENDENCY_CHECK_NONE = 0;
public static final int DEPENDENCY_CHECK_OBJECTS = 1;
public static final int DEPENDENCY_CHECK_SIMPLE = 2;
public static final int DEPENDENCY_CHECK_ALL = 3;
// 關閉應用上下文時需呼叫的方法名稱
public static final String INFER_METHOD = "(inferred)";
// 存放 Bean 的 Class 物件
private volatile Object beanClass;
// Bean 的作用範圍
private String scope = SCOPE_DEFAULT;
// 非抽象
private boolean abstractFlag = false;
// 非延遲載入
private boolean lazyInit = false;
// 預設不自動裝配
private int autowireMode = AUTOWIRE_NO;
// 預設不依賴檢查
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
// 依賴的 Bean 列表
private String[] dependsOn;
// 可以作為自動裝配的候選者,意味著可以自動裝配到其他 Bean 的某個屬性中
private boolean autowireCandidate = true;
// 建立當前 Bean 例項工廠類名稱
private String factoryBeanName;
// 建立當前 Bean 例項工廠類中方法名稱
private String factoryMethodName;
// 儲存構造方法的引數
private ConstructorArgumentValues constructorArgumentValues;
// 儲存 Bean 屬性名稱以及對應的值
private MutablePropertyValues propertyValues;
// 儲存被覆蓋的方法資訊
private MethodOverrides methodOverrides;
// init、destroy 方法名稱
private String initMethodName;
private String destroyMethodName;
// 是否執行 init 和 destroy 方法
private boolean enforceInitMethod = true;
private boolean enforceDestroyMethod = true;
// Bean 是否是使用者定義的而不是應用程式本身定義的
private boolean synthetic = false;
// Bean 的身份類別,預設是使用者定義的 Bean
private int role = BeanDefinition.ROLE_APPLICATION;
// Bean 的描述資訊
private String description;
// Bean 定義的資源
private Resource resource;
...
}
以上是 AbstractBeanDefinition 中定義的一些常量和屬性,該類中還有一部分是操作這些屬性的 set 和 get 方法,這些方法都由子類來操作,且應用程式中真正使用的也是這些子類 BeanDefinition。
先來看 AbstractBeanDefinition 直接實現類:RootBeanDefinition、GenericBeanDefinition、ChildBeanDefinition。
RootBeanDefinition
該類繼承自 AbstractBeanDefinition,它可以單獨作為一個 BeanDefinition,也可以作為其他 BeanDefinition 的父類。
RootBeanDefinition 在 AbstractBeanDefinition 的基礎上定義了更多屬性。
public class RootBeanDefinition extends AbstractBeanDefinition {
// BeanDefinitionHolder 儲存 Bean 的名稱、別名、BeanDefinition
private BeanDefinitionHolder decoratedDefinition;
// AnnotatedElement 是java反射包的介面,通過它可以檢視 Bean 的註解資訊
private AnnotatedElement qualifiedElement;
// 允許快取
boolean allowCaching = true;
// 工廠方法是否唯一
boolean isFactoryMethodUnique = false;
// 封裝了 java.lang.reflect.Type,提供了泛型相關的操作
volatile ResolvableType targetType;
// 快取 Class,表示 RootBeanDefinition 儲存哪個類的資訊
volatile Class<?> resolvedTargetType;
// 快取工廠方法的返回型別
volatile ResolvableType factoryMethodReturnType;
// 這是以下四個構造方法欄位的通用鎖
final Object constructorArgumentLock = new Object();
// 用於快取已解析的構造方法或工廠方法
Executable resolvedConstructorOrFactoryMethod;
// 將構造方法引數標記為已解析
boolean constructorArgumentsResolved = false;
// 用於快取完全解析的構造方法引數
Object[] resolvedConstructorArguments;
// 快取待解析的構造方法引數
Object[] preparedConstructorArguments;
// 這是以下兩個後處理欄位的通用鎖
final Object postProcessingLock = new Object();
// 表明是否被 MergedBeanDefinitionPostProcessor 處理過
boolean postProcessed = false;
// 在生成代理的時候會使用,表明是否已經生成代理
volatile Boolean beforeInstantiationResolved;
// 實際快取的型別是 Constructor、Field、Method 型別
private Set<Member> externallyManagedConfigMembers;
// InitializingBean中 的 init 回撥函式名 afterPropertiesSet 會在這裡記錄,以便進行生命週期回撥
private Set<String> externallyManagedInitMethods;
// DisposableBean 的 destroy 回撥函式名 destroy 會在這裡記錄,以便進生命週期回撥
private Set<String> externallyManagedDestroyMethods;
...
}
ChildBeanDefinition
該類繼承自 AbstractBeanDefinition。其相當於一個子類,不可以單獨存在,必須依賴一個父 BeanDetintion,構造 ChildBeanDefinition 時,通過構造方法傳入父 BeanDetintion 的名稱或通過 setParentName 設定父名稱。它可以從父類繼承方法引數、屬性值,並可以重寫父類的方法,同時也可以增加新的屬性或者方法。若重新定義 init 方法,destroy 方法或者靜態工廠方法,ChildBeanDefinition 會重寫父類的設定。
從 Spring 2.5 開始,以程式設計方式註冊 Bean 定義的首選方法是 GenericBeanDefinition,GenericBeanDefinition 可以有效替代 ChildBeanDefinition 的絕大分部使用場合。
GenericBeanDefinition
GenericBeanDefinition 是 Spring 2.5 以後新引入的 BeanDefinition,是 ChildBeanDefinition 更好的替代者,它同樣可以通過 setParentName 方法設定父 BeanDefinition。
最後三個 BeanDefinition 既實現了 AnnotatedBeanDefinition 介面,又間接繼承 AbstractBeanDefinition 抽象類,這些 BeanDefinition 描述的都是註解形式的 Bean。
ConfigurationClassBeanDefinition
該類繼承自 RootBeanDefinition ,並實現了 AnnotatedBeanDefinition 介面。這個 BeanDefinition 用來描述在標註 @Configuration 註解的類中,通過 @Bean 註解例項化的 Bean。
其功能特點如下:
1、如果 @Bean 註解沒有指定 Bean 的名字,預設會用方法的名字命名 Bean。
2、標註 @Configuration 註解的類會成為一個工廠類,而標註 @Bean 註解的方法會成為工廠方法,通過工廠方法例項化 Bean,而不是直接通過構造方法初始化。
3、標註 @Bean 註解的類會使用構造方法自動裝配
AnnotatedGenericBeanDefinition
該類繼承自 GenericBeanDefinition ,並實現了 AnnotatedBeanDefinition 介面。這個 BeanDefinition 用來描述標註 @Configuration 註解的 Bean。
ScannedGenericBeanDefinition
該類繼承自 GenericBeanDefinition ,並實現了 AnnotatedBeanDefinition 介面。這個 BeanDefinition 用來描述標註 @Component 註解的 Bean,其派生註解如 @Service、@Controller 也同理。
總結
最後,我們來做個總結。BeanDefinition 主要是用來描述 Bean,其儲存了 Bean 的相關資訊,Spring 例項化 Bean 時需讀取該 Bean 對應的 BeanDefinition。BeanDefinition 整體可以分為兩類,一類是描述通用的 Bean,還有一類是描述註解形式的 Bean。一般前者在 XML 時期定義 <bean‘> 標籤以及在 Spring 內部使用較多,而現今我們大都使用後者,通過註解形式載入 Bean。
以上就是本章內容,如果文章中有錯誤或者需要補充的請及時提出,本人感激不盡。
參考:
http://cmsblogs.com/?p=11731
https://cloud.tencent.com/developer/article/1497