引入
在前面的文章中, 咱們講解到Spring在真正建立一個bean的時候是經過調用一個lamada表達式中的createBean
方法來建立的, 而且咱們以前也對這個createBean方法中的resolveBeforeInstantiation方法進行了詳細的
講解(完成SpringAOP的初始化工做, 設置了不須要參與AOP的類), 日後咱們又經過createBean中的doCreateBean
方法對Spring建立bean實例的源碼進行了分析, 即createBeanInstance方法, 經過上面的流程, Spring已經
建立好了bean實例, 在doCreateBean方法中, Spring調用完createBeanInstance方法後又調用了一次後置處
理器, 即咱們本篇內容須要講解的方法-applyMergedBeanDefinitionPostProcessors
複製代碼
applyMergedBeanDefinitionPostProcessors源碼分析
- applyMergedBeanDefinitionPostProcessors
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof MergedBeanDefinitionPostProcessor) {
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
}
分析:
根據代碼能夠看到, Spring其實就是調用了全部類型爲MergedBeanDefinitionPostProcessor後置處理器的
postProcessMergedBeanDefinition方法, 在沒有手動的添加bean的後置處理器的條件下, 筆者分析發現,
實現了該方法的後置處理器有如下幾個:
ApplicationListenerDetector
ScheduledAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor
InitDestroyAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor
通過一個個的源碼查看, 發現ScheduledAnnotationBeanPostProcessor和RequiredAnnotationBeanPostProcessor
兩個後置處理器都僅僅是對postProcessMergedBeanDefinition方法進行了空實現, 而ApplicationListenerDetector
雖然進行了實現, 可是其裏面代碼就一行:
this.singletonNames.put(beanName, beanDefinition.isSingleton());
即將一個bean是不是單例設置到了singletonNames這個map中, 而這個map是在ApplicationListenerDetector
中的, 因此對於前三個後置處理器來講, 咱們能夠跳過了....
在真正分析後三個後置處理器以前, 咱們首先從總體上說一下, 後三個後置處理器的工做都差很少, 都是爲了
查找出知足條件的屬性、方法, 將他們封裝起來, 以便後面在填充屬性的時候能夠直接使用, 在分析源碼以前,
咱們先來聊聊幾個源碼中會出現的類的做用
複製代碼
Member、InjectedElement、InjectionMetadata
在Java反射中, 咱們會常常的遇到Field、Method、Constructor類, 而本次咱們提到的第一個類就是Member,
該類就是上面幾個類的父類, Spring爲了可以使得獲取到的方法、屬性都放在一個地方, 採用了接口編程, 將其
都變成了Member類型
當Spring掃描到一個方法加了@Autowired的時候, 就會將該方法反射得到到Method變爲一個Member, 而後將其
放到InjectedElement中, 換句話說, InjectedElement就是對一個方法或者屬性的一個封裝, 除了有Member
存儲原始的反射信息外, 還會有額外的信息, 好比required屬性, 表示是不是必須注入的
一個類中可能會有不少個方法、屬性被標註了@Autowired註解, 那麼每個被標註的方法、屬性都用一個
InjectedElement表示, 而全部這些InjectedElement均被放入到一個Collections中, 這個集合則存在於
InjectionMetadata中, 即InjectionMetadata中的Collection<InjectedElement> injectedElements存儲
了全部須要被注入的信息, 裏面有一個targetClass屬性則是存儲了這些方法、屬性所在的類Class對象
public class InjectionMetadata {
private final Class<?> targetClass;
private final Collection<InjectedElement> injectedElements;
private volatile Set<InjectedElement> checkedElements;
}
能夠看到, 在InjectionMetadata中還有一個checkedElements, 裏面也是存儲了InjectedElement, 以前提到
injectedElements的時候, 有人可能會認爲, 難道Spring在後面進行屬性填充的時候, 就是取injectedElements
中的一個個InjectedElement進行反射操做進行注入的嗎, 其實不是的, Spring實際取的是checkedElements中
的InjectedElement, 在MergedBeanDefinitionPostProcessor中postProcessMergedBeanDefinition方法
中, Spring主要是找到全部被@PostConstruct、@PreDestory、@Autowired、@Resource、@Value標註的屬性
或者方法, 將其封裝成一個個的InjectedElement, 最後放到一個新建立的InjectedMetada中, 完成這些工做
後, Spring又會通過一些判斷, 最終將這些InjectedElement從injectedElements取出來放到checkedElements
中, 在進行屬性填充的時候, Spring就會取出一個個的InjectedElement, 經過反射的方式完成屬性填充, 那麼
上述提到的三個後置處理器有什麼做用呢, 其實這是一個策略模式的典型應用, Spring對@PostConstruct、
@PreDestory註解的處理(轉爲InjectedElement)用的InitDestroyAnnotationBeanPostProcessor, 而對
@Resource的處理用的CommonAnnotationBeanPostProcessor, 對於@Autowired以及@Value的處理則是用的
AutowiredAnnotationBeanPostProcessor, 不一樣的後置處理器處理不一樣的註解, 下面咱們以@Autowired註解
的處理爲例子進行講解, 其它註解的處理的代碼跟這個是相似的, 就再也不進行展開了
複製代碼
AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition方法
- postProcessMergedBeanDefinition方法
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
分析:
findAutowiringMetadata方法完成了@Autowired註解的處理, 將被該註解標註的屬性、方法封裝爲一個個的
InjectedElement, 而後放入到InjectionMetadata中的集合injectedElements中
checkConfigMembers方法則將injectedElements中的一個個InjectedElement取出來, 進行一些判斷, 最
後放入到checkedElements這個Set中(屬性注入的時候就是取得checkedElements中得InjectedElement)
下面咱們分別來說解下這兩個方法
複製代碼
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
分析:
Spring會先從緩存injectionMetadataCache中獲取當前bean對應得注入元數據, 若是needsRefresh返回了
true, 那麼Spring就會調用buildAutowiringMetadata方法開始構建注入元數據, 構建完成後就會將其放入
到緩存injectionMetadataCache中了
needsRefresh的判斷很簡單, 即metadata == null || metadata.targetClass != clazz
當metadata不存在於緩存的時候確定是要進行構建的, 因爲findAutowiringMetadata方法會在屬性注入的時
候也被調用, 因此一般狀況下會拿到緩存中的數據, 須要注意的是, 在上述後置處理器調用完成後, 若是程序
員手動的修改了InjectedMetadata中的targetClass, 那麼就不能用原來的元數據了, 而是要從新構建一次,
這也是metadata.targetClass != clazz返回true的狀況下Spring也會調用構建方法的緣由
複製代碼
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
..........獲取一個個的InjectedElement..........
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
分析:
能夠看到, Spring會建立一個elements的list來存放全部須要被注入的數據, 而後在while循環中, 開始獲取
該targetClass中的元數據, 獲取完成後放入到elements中, 以後開始獲取targetClass的父類中素有須要
被注入的數據, 直到Object爲止
當一個類及其全部的祖先類中的元數據被掃描完成後, Spring就會將其放入到InjectionMetadata中返回,
接下來咱們開始分析Spring在這個while循環是如何獲取一個個的InjectedElement的
複製代碼
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
for (Field field : getDeclaredFields(clazz)) {
fc.doWith(field);
}
}
分析:
根據上面的代碼能夠看到, Spring會遍歷一個個的屬性, 而後獲取到該屬性上@Autowired註解, 若是該註解
不爲空, 則Spring會將其變成一個AutowiredFieldElement(繼承於InjectedElement), 而後將其添加到
currentElements中, 在此之間, Spring還會判斷@Autowired註解中required屬性, 判斷是不是該屬性的
注入是必須的, 若是一個屬性是static的, 那麼就直接返回了, 而且會打印一個info日誌(筆者沒寫出來)
複製代碼
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
return;
}
boolean required = determineRequiredStatus(ann);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
分析:
在上面代碼中, 其實咱們能夠看到, 其實對@Autowired標註的方法和以前對屬性的處理是相似的, 都是對一個
個的method進行循環, 而後一個個處理, 若是一個方法是static的則直接返回不進行處理了, 上面出現了一個
橋接方法的定義, 筆者不太清楚這個橋接方法是什麼, 一般狀況下是直接返回咱們在類中定義的method的, 再
日後, Spring調用了findPropertyForMethod獲取屬性裝飾器, 這裏簡單的擴展一下, PropertyDescriptor
實際上是屬於Java層面的知識, 屬於Java內省機制的一部分, 其實就是Java中的一些定義而已, 在java中, 定
義一個setXXX, getXXX, isXXX方法爲屬性的描述, Java中約定這些方法名中除了set、get、is以後的字母,
則是Java中的屬性名稱(僅僅是一種約定而已, 咱們不必定要遵照), 用PropertyDescriptor類上的註釋描述
爲: A PropertyDescriptor describes one property that a Java Bean exports via a
pair of accessor methods.
總之, findPropertyForMethod方法則是獲取該方法所在類中的全部get、set、is方法的屬性描述, 若是當前
被遍歷的方法屬於其中一類, 則返回該方法的PropertyDescriptor, 若是不是, 好比checkXXX, 則返回null
複製代碼
在buildAutowiringMetadata方法中, Spring將一個類中@Autowired註解標註的方法和屬性變爲了一個個的
InjectedElement, 放入到一個elements的集合中, 最後將這個集合放入到InjectionMetadata中並返回
複製代碼
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
Set<InjectedElement> checkedElements = new LinkedHashSet<>(this.injectedElements.size());
for (InjectedElement element : this.injectedElements) {
Member member = element.getMember();
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
beanDefinition.registerExternallyManagedConfigMember(member);
checkedElements.add(element);
}
}
this.checkedElements = checkedElements;
}
分析:
能夠看到, Spring遍歷findAutowiringMetadata方法中找出來的一個個InjectedElement, 若是其知足代碼
中的條件的話, 就將其放入到checkedElements中, 而這個條件以下:
public boolean isExternallyManagedConfigMember(Member configMember) {
synchronized (this.postProcessingLock) {
return (this.externallyManagedConfigMembers != null &&
this.externallyManagedConfigMembers.contains(configMember));
}
}
public void registerExternallyManagedConfigMember(Member configMember) {
synchronized (this.postProcessingLock) {
if (this.externallyManagedConfigMembers == null) {
this.externallyManagedConfigMembers = new HashSet<>(1);
}
this.externallyManagedConfigMembers.add(configMember);
}
}
分析:
能夠看到, 當externallyManagedConfigMembers中不存在這個InjectElement的member(Method/Field)時,
則調用registerExternallyManagedConfigMember方法放入進去, 若是出現兩個如出一轍的member, 則只會
放入一個, 或許checkConfigMembers方法就是對InjectedElement的一種去重吧
複製代碼
總結
applyMergedBeanDefinitionPostProcessors方法經過調用一個個的MergedBeanDefinitionPostProcessor
中的postProcessMergedBeanDefinition方法完成了對被@Autowired等註解標註的方法、屬性的處理, 將其變
爲一個個的InjectionMetadata, 最終放入到該後置處理器中的injectionMetadataCache緩存中, 以後即可以
經過該後置處理器取得這些注入元數據, 進而完成屬性的注入, 這裏Spring也經過策略模式, 對不一樣類型的元數
據利用不一樣的後置處理器進行處理
複製代碼