一 . 前言
這一篇來看看 SpringIOC 裡面的一個細節點 , 來簡單看看 BeanDefinition 這個對象 , 以及有沒有辦法對其進行定制.
二. BeanDefinition 的體系
2.1 體系概覽
這裡面需要關注的幾個類分别為 :
- BeanDefinition 接口 : 頂層接口 , 抽象了Bean加載的方法
- AbstractBeanDefinition : 提供了多數方法的預設實作
- RootBeanDefinition : Spring BeanFactory 運作時統一的 BeanDefinition 視圖
- GenericBeanDefinition : 程式設計方式注冊 BeanDefinition 的首選類
- ChildBeanDefinition : 可繼承BeanDefinition
下面來解釋一下這裡面說的一些概念 :
什麼叫統一視圖 ?
稍微從跟蹤一下源碼就能發現 , 從 xml 或者 JavaConfig 以及 Spring 預設加載的Bean配置類 ,最終都會被修飾為 RootBeanDefinition
GenericBeanDefinition 怎麼用 ?
GenericBeanDefinition 是通過程式設計方式注入的 BeanDefinition 所對應的類 ,通常都是該類的子類 , 包括非Spring 的 ConfigBean 和 ServiceBean
ChildBeanDefinition 又做了什麼 ?
一種可以繼承 parent 配置的 BeanDefinition , 在加載環節中會通過 AbstractBeanFactory#getMergedLocalBeanDefinition() 來将 child 和 parent bean definition 進行合并。
- BeanDefinition 進行 merge 操作時,會将 child 的屬性與 parent 的屬性進行合并,當有相同屬性時,以 child 的為準
- 如果是 Map 形式的配置 , 會取并集
2.2 BeanDefinition 的作用
- 存儲屬性 : 基于接口 AttributeAccessor 實作
- 存儲中繼資料配置 : 基于 BeanMetadataElement 實作
- 描述類的資訊 : 包括Bean名稱 , Primary 屬性 , priority 配置 等等
- Bean 的加載 : 例如 getBeansOfType , getBean 等等
總結其實就是一句話 : BeanDefinition 主要承載了Bean的中繼資料資訊 ,同時描述了Bean在Spring體系中的加載方式 , 容器通過 BeanDefinition 中的配置來加載一個Bean
三. BeanDefinition 的載入
3.1 載入的入口
S1 : 啟動配置類的載入
Spring 中第一個載入的 BeanDefinition 即為 RootBeanDefinition , 主要通過 AnnotationConfigUtils # registerAnnotationConfigProcessors 方法進行加載
在這個環節中會通過加載的方式分别載入多個不同的 RootBeanDefinition , 這裡是 Contain 關系 :
// internalConfigurationAnnotationProcessor
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
// 建構對應的 PostProcessor 并且載入
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// internalAutowiredAnnotationProcessor
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
//.....
}
複制代碼
在這個環節中 , 基本上都是在通過 registerPostProcessor 來注冊各類加載類 , 我把這些看成根類 .
這些類通常為 Spring 進行服務 , 用來配置各類資訊和加載封裝Bean
S2 : 普通配置類的載入
普通類的載入包括 SpringApplication 和一些自定義的個人配置類 , 這些類主要為了對非 Spring 的元件進行注冊 , 配置 , 注入等操作
這一類通常通過 registerBean 來實作Bean的注冊 , 注冊的入口也很多 :
包括 AnnotatedBeanDefinitionReader 和 ConfigurationClassPostProcessor等, 不難發現這一類 BeanDefinition 通常都是由 RootBeanDefinition 裝載的類進行載入的
通常注冊出來的對象也為 AnnotatedGenericBeanDefinition 和 GenericBeanDefinition 的子類等
3.2 儲存的邏輯
BeanDefinition 會在 DefaultListableBeanFactory # registerBeanDefinition 中進行注冊.
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// S1 : 會對 BeanDefinition 進行校驗 , 主要是MethodOverrides和FactoryMethodName不能同時存在
// -- 工廠方法必須建立具體的 Bean 執行個體 , 而 methodOverrides 會建立代理類且進行增強
// -- 也就是說 工廠需要執行個體 , 不能是代理類
if (beanDefinition instanceof AbstractBeanDefinition) {
((AbstractBeanDefinition) beanDefinition).validate();
}
// S2 : 判斷 BeanDefinition 是否已經存在
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// 是否允許同名Bean重寫 , 因為此處已經存在一個了 , 不能重寫則直接異常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// 角色比較 ,隻打日志
// ROLE_APPLICATION / ROLE_SUPPORT / ROLE_INFRASTRUCTURE
}else if (!beanDefinition.equals(existingDefinition)) {
// 判斷是否為同一對象
}
// 以上主要是打log , 這裡如果允許覆寫則直接覆寫了
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 判斷該Bean是否已經開始初始化
if (hasBeanCreationStarted()) {
// 如果已經開始 , 需要對 Map 上鎖後再處理
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
// 省略一些更新操作
}
}
else {
// 沒有加載時的載入
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 如果Bean已經存在或已經開始加載了 , 這個時候時需要進行銷毀操作的
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
protected void resetBeanDefinition(String beanName) {
// S1 : 如果已經建立 ,需要清空 Merge BeanDefinition
clearMergedBeanDefinition(beanName);
// S2 : 銷毀 Bean
destroySingleton(beanName);
// S3 : 調用 PostProcessors 重置處理器進行處理
for (BeanPostProcessor processor : getBeanPostProcessors()) {
if (processor instanceof MergedBeanDefinitionPostProcessor) {
((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);
}
}
// S4 : 如果該 BeanDefinition 是某個BeanDefinition 的Parent , 則需要同步處理
for (String bdName : this.beanDefinitionNames) {
if (!beanName.equals(bdName)) {
BeanDefinition bd = this.beanDefinitionMap.get(bdName);
if (bd != null && beanName.equals(bd.getParentName())) {
resetBeanDefinition(bdName);
}
}
}
}
複制代碼
3.3 使用的方式
BeanDefinition 的批量處理流程也是在 DefaultListableBeanFactory 中進行的
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 對所有的 BeanDefinition 進行循環處理
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 排除掉懶加載的Bean
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 此處省略工廠類的判斷及處理 ...
// 進入Bean擷取邏輯
getBean(beanName);
}
}
//.....
}
複制代碼
總結
分析 BeanDefinition 不是這階段的主要目的 , 後續會有幾篇應用的文章來着重思考下如何進行業務定制
其實寫源碼文章是最輕松的 , 看懂就完事了 , 而寫定制或者業務 , 往往寫着寫着發現有地方沒搞懂 , 就需要回頭繼續看這個點 , 難度要大得多.......
作者:AntBlack
連結:https://juejin.cn/post/7157695463415087140