Spring 注解原理(二)AutowiredAnnotationBeanPostProcessor:@Autowired @Value @Inject @Lookup
Spring 系列目錄(https://www.cnblogs.com/binarylei/p/10198698.html)
AutowiredAnnotationBeanPostProcessor 是 Spring 注解驅動的核心組件之一,都是處理的 Bean 的依賴注入,相關的注解有 @Autowired @Value @Inject @Lookup 四個,也可以自定義注解后添到 autowiredAnnotationTypes 集合中。
- @Autowired @Value @Inject:這三個注解的邏輯完全一樣,都是處理依賴注入,其優先級 @Autowired > @Value > @Inject。因此本文在此做如下約定,@Autowired 一般指的是這三個注解。
- @Lookup:本質也是解決依賴注入的問題,但和上面三個注解不同的是,@Lookup 注入的對象是動態的( 尤其是 prototype 實例),而 @Autowired 注入的對象是靜態的,一旦注入就不可發生改變。@Lookup 只能標注在抽象方法上,實例化時使用 CglibSubclassingInstantiationStrategy 進行字節碼提升,每次調用該抽象方法時,都調用 beanFactory#getBean 重新獲取對象。
1. 工作原理
現在我們先大致看一下 AutowiredAnnotationBeanPostProcessor 是如何工作的呢?beanFactory#doCreateBean 在創建 bean 過程中依次調用如下方法:
- determineCandidateConstructors:解析類的構造器,用於處理構造器注入。如果構造器上標注有 @Autowired 注解,或只有一個有參構造器,則采用構造器自動注入。否則完全按照默認的配置參數 bd. constructorArgumentValues 實例化對象,或無參構造器實例化對象。
- postProcessMergedBeanDefinition:配合 postProcessPropertyValues 方法一起處理字段或方法注入。解析標注有 @Autowired 的注入點元信息 InjectionMetadata,底層調用 findAutowiringMetadata 方法解析注入點元信息。
- postProcessPropertyValues:將 postProcessMergedBeanDefinition 階段解析的 InjectionMetadata 依次進行屬性注入。
AbstractAutowireCapableBeanFactory#doCreateBean
-> createBeanInstance
-> determineConstructorsFromBeanPostProcessors
-> AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors
-> autowireConstructor
-> ConstructorResolver#autowireConstructor
-> applyMergedBeanDefinitionPostProcessors
-> AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
-> populateBean
-> AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues
2. determineCandidateConstructors
determineCandidateConstructors 處理構造器注入的場景。該方法解析類的構造器,如果構造器上標注有 @Autowired 注解,或只有一個有參構造器,則采用構造器自動注入。否則完全按照默認的配置參數 bd. constructorArgumentValues 實例化對象,或無參構造器實例化對象。
-
@Lookup:實例化時使用 CglibSubclassingInstantiationStrategy 進行字節碼提升,生成代理對象。
-
@Autowired:如果構造器上標注有 @Autowired(required=false) 注解,則添加到候選構造器 candidates 中,最后再將默認的無參構造器(如果存在)添加到 candidates 中返回即可。
但如果有標注 @Autowired 的候選構造器,則標注有 @Autowired 注解的候選構造器只能有一個,並最終返回這個候選構造器。
-
無 @Autowired:如果只有一個有參構造器,則直接返回這個構造器即可。如果有多個構造器或只有一個默認構造器,則返回 null。
@Override
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException {
// 1. 校驗@Lookup 注解 省略...
// 2. 解析@Autowire標注的構造器
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
Constructor<?>[] rawCandidates = beanClass.getDeclaredConstructors();
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
Constructor<?> requiredConstructor = null;
Constructor<?> defaultConstructor = null;
int nonSyntheticConstructors = 0;
// 3.1 遍歷所有的構造器
for (Constructor<?> candidate : rawCandidates) {
// 3.2 構造器上是否標注有@Autowire,注意CGLIB代理
MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
if (ann == null) {
Class<?> userClass = ClassUtils.getUserClass(beanClass);
if (userClass != beanClass) {
Constructor<?> superCtor = userClass.getDeclaredConstructor(candidate.getParameterTypes());
ann = findAutowiredAnnotation(superCtor);
}
}
// 3.3 如果標注@Autowire,進一步判斷是否require=true,如果為true,只能有一個
if (ann != null) {
if (requiredConstructor != null) {
throw new BeanCreationException();
}
boolean required = determineRequiredStatus(ann);
if (required) {
if (!candidates.isEmpty()) {
throw new BeanCreationException();
}
requiredConstructor = candidate;
}
candidates.add(candidate);
// 3.4 緩存默認構造器
} else if (candidate.getParameterCount() == 0) {
defaultConstructor = candidate;
}
}
// 3.5 結果處理
// 3.5.1 標注@Autowire,如果require=true直接返回。否則添加無參構造器返回
if (!candidates.isEmpty()) {
if (requiredConstructor == null) {
if (defaultConstructor != null) {
candidates.add(defaultConstructor);
}
}
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
// 3.5.2 無@Autowire。如果只有一個有參構造器,返回這個構造器,自動注入
} else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
// 3.5.3 無@Autowire。如果有多個構造器或只有一個無參構造器,返回null
} else {
candidateConstructors = new Constructor<?>[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
說明: determineCandidateConstructors 解析類的構造器,我們主要看一下結果是如何處理的。
- 有構造器上標注 @Autowire。根據屬性值 require 又可以分為兩種情況,指定是否是必須的構造器,默認為 true。
- require=true:只能有一個構造器設置為必須構造器,直接使用這個構造器實例化對象。
- require=false:可以標注多個候選構造器,需要根據參數進一步匹配具體的構造器。此時,會添加默認的無參構造器。
- 沒有構造器標注 @Autowire。也可以分為兩種情況:
- 只有一個有參構造器:直接返回這個有參構造器。
- 有多個構造器或只有一個無參構造器:返回 null。此時需要根據 bd 配置來實例化對象。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
...
// 1. 解析是否有構造器可用,當 ctors!=null 時采用即構造器自動注入
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 2. 有參構造器實例化(大部分情況),采用即構造器自動注入
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// 3. 無參構造器實例化
return instantiateBean(beanName, mbd);
}
3. postProcessMergedBeanDefinition
postProcessMergedBeanDefinition 和 postProcessMergedBeanDefinition 處理字段或方法注入的場景。postProcessMergedBeanDefinition 方法將標注 @Autowired 注入點(字段或方法)解析成元信息 InjectionMetadata,postProcessMergedBeanDefinition 則根據元信息 InjectionMetadata 注入到 bean 中。
字段或方法注入相關方法說明:
- postProcessMergedBeanDefinition:將 @Autowired 注入點(字段或方法)解析成元信息 InjectionMetadata。
- findAutowiringMetadata:緩存解析后的注入點元信息到 injectionMetadataCache。
- buildAutowiringMetadata:遞歸遍歷所有的字段和方法,將標注 @Autowired 的注入點解析成 InjectionMetadata。該方法不會解析靜態字段,所以 @Autowired 無法注入靜態字段。
- 字段:AutowiredFieldElement
- 方法:AutowiredMethodElement
- InjectionMetadata#checkConfigMembers:將已經處理過的注入點緩存到 bd.externallyManagedConfigMembers 中,下次再處理時不會處理已經緩存的注入點。
- InjectionMetadata#inject:依次遍歷所有的注入點元信息 InjectedElement,進行屬性注入。
AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
-> findAutowiringMetadata
-> buildAutowiringMetadata
-> InjectionMetadata#checkConfigMembers
AutowiredAnnotationBeanPostProcessor#postProcessProperties
-> InjectionMetadata#inject
3.1 buildAutowiringMetadata
buildAutowiringMetadata 方法遞歸遍歷所有的字段和方法,將標注 @Autowired 的注入點解析成 InjectionMetadata。該方法不會解析靜態字段,所以 @Autowired 無法注入靜態字段。方法本身並不難理解,最重要的關心解析后的對象 AutowiredFieldElement 和 AutowiredMethodElement。
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
// 1. 校驗,如果clazz是JDK中的類,直接忽略,因為不可能標注有這些標注
if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
return InjectionMetadata.EMPTY;
}
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
// 遞歸循環所有的父類,所有@Autowired父類的字段也會自動注入
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 2. 解析所有字段上的注解,封裝成AutowiredFieldElement,不包括static字段
ReflectionUtils.doWithLocalFields(targetClass, field -> {
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
if (ann != null) {
if (Modifier.isStatic(field.getModifiers())) {
return;
}
boolean required = determineRequiredStatus(ann);
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 3. 解析所有方法上的注解,封裝成AutowiredMethodElement,不包括static方法
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 3.1 處理橋接方法,先忽略這部分
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// 3.2 忽略static方法
if (Modifier.isStatic(method.getModifiers())) {
return;
}
boolean required = determineRequiredStatus(ann);
// 3.3 如果是JavaBean字段,則返回pd,否則返回null
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
} while (targetClass != null && targetClass != Object.class);
return InjectionMetadata.forElements(elements, clazz);
}
說明: buildAutowiringMetadata 字段和方法注入點的元信息解析大同小異:
- 遞歸解析所有的父類的字段和方法,所以父類可以通過 @Autowired 注入。
- 不會解析靜態字段或方法,所以靜態字段無法通過 @Autowired 注入。
- 字段和方法分別解析為 AutowiredFieldElement 和 AutowiredMethodElement。其中有兩個重要的屬性:一個是注入點 Member,二是注入點是否必須。
4. postProcessPropertyValues
postProcessMergedBeanDefinition 方法根據元信息 InjectionMetadata,在 Spring IoC 容器中查找依賴注入到 bean 中。該方法完全委托給了 InjectionMetadata#inject 方法:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
for (InjectedElement element : elementsToIterate) {
element.inject(target, beanName, pvs);
}
}
}
說明: inject 方法就是循環所有的注入點,依次調用其 inject 進行屬性注入。問題的關鍵是 checkedElements 是什么,也就是說會注入那些字段?在之前分析 postProcessMergedBeanDefinition 時,提到調用 findAutowiringMetadata 解析完注入點元信息后,會調用 InjectionMetadata#checkConfigMembers 校驗。校驗到底是做什么的呢?
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;
}
說明: bd.externallyManagedConfigMembers 緩存已經校驗過的注入點,這些緩存的注入點不會再次進行注入,目的就是為了避免重復注入的問題。那問題就來了,字段怎么會進行重復注入呢?比如 CommonAnnotationBeanPostProcessor 同樣會解析注入點的元信息,如果 @Autowired 和 @Resource 出現在同一個字段上,此時會出現重復注入的情況。
下面對 InjectionMetadata 中兩個注入點屬性進行一下說明:
- injectedElements:所有解析的注入點元信息 InjectedElement。
- checkedElements:需要進行屬性注入的注入元信息,剔除 bd.externallyManagedConfigMembers 已經處理的注入點。
下面會對字段注入和方法注入,分別進行分析。關鍵是如何進行依賴查找,底層最終都是調用 beanFactory#resolveDependency 方法。
5. AutowiredFieldElement
字段注入時,通常根據字段類型和字段名稱查找依賴。當然,如果你使用 @Value("#{beanName}") 時,會讀取注解中的值進行解析。核心還是 beanFactory#resolveDependency 方法。方法本身很簡單,都不多說了。
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
// 從緩存中提取value值,可能為desc、beanName、value值
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
} else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
synchronized (this) {
// 只要是解析過,不會緩存下來
if (!this.cached) {
if (value != null || this.required) {
// 緩存DependencyDescriptor
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
// 緩存名稱beanName,直接根據名稱查找
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
} else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
// java反射
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
6. AutowiredMethodElement
基本上和 AutowiredFieldElement 雷同,唯一不同的是方法注入時,需要對方法的所以參數依次調用 beanFactory#resolveDependency 進行依賴查找。根據 require 值,如果為 true 時無法查找到依賴時會繼續查找,false 則不再進行查找。一般默認為 true。
每天用心記錄一點點。內容也許不重要,但習慣很重要!