Spring 源碼閲讀 41:AutowiredAnnotationBeanPostProcessor 分析(2)

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第12天,點擊查看活動詳情

基於 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源碼閲讀 40:ConfigurationClassPostProcessor 分析(1)

概述

上一篇介紹了 AutowiredAnnotationBeanPostProcessor 後處理器中的determineCandidateConstructors。這個方法為 Spring 通過反射創建 Bean 實例是提供了候選的構造方法。

本文開始分析 AutowiredAnnotationBeanPostProcessor 中另一個比較重要的處理方法postProcessMergedBeanDefinition,它被調用的時機是在 Spring 通過反射創建 Bean 實例對象之後、屬性裝配之前。它的作用,是將類中標記了相關注解的注入點解析出來。

postProcessMergedBeanDefinition方法分析

進入postProcessMergedBeanDefinition方法的源碼。

// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); }

非常簡潔,只有兩行代碼,分別調用了兩個方法,我們逐個分析。

findAutowiringMetadata

先進入findAutowiringMetadata方法。

// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) { // Fall back to class name as cache key, for backwards compatibility with custom callers. String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName()); // Quick check on the concurrent map first, with minimal locking. 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; }

從方法的名稱中可以看出,它用來從類中找到自動注入元信息,最後返回一個 InjectionMetadata 類型的結果。

首先,會從緩存injectionMetadataCache中,查找注入元信息,如果獲取到的注入元信息不需要被刷新,則直接返回。如果需要刷新,則通過buildAutowiringMetadata方法,構建元信息,並放入緩存中,再在方法末尾返回。

在這裏,如果緩存中獲取到的注入元信息為空,也屬於需要刷新的情況。

接下來我們看buildAutowiringMetadata方法是如何構建這些元信息的。

這個方法比較長,我們逐步來分析。

if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) { return InjectionMetadata.EMPTY; }

首先會判斷當前的類型是不是 Java 內部的類型,如果是的話,則直接返回空的信息。

``` List elements = new ArrayList<>(); Class<?> targetClass = clazz;

do { // 循環語句塊 } while (targetClass != null && targetClass != Object.class); ```

接着聲明瞭一個 InjectionMetadata.InjectedElement 類型的空列表,以及一個表示類型的targetClass變量,初始值為方法參數傳入的clazz。然後,在targetClass不為空且不是 Object 類型的情況下,執行do-while循環中的邏輯。

下面我們看do-while語句塊中的內容。

``` final List currElements = new ArrayList<>();

ReflectionUtils.doWithLocalFields(targetClass, field -> { MergedAnnotation<?> ann = findAutowiredAnnotation(field); if (ann != null) { if (Modifier.isStatic(field.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static fields: " + field); } return; } boolean required = determineRequiredStatus(ann); currElements.add(new AutowiredFieldElement(field, required)); } }); ```

在循環語句塊中,首先會遍歷所有的被標記了@AutoWired@Value@Inject註解的字屬性字段封裝為 AutowiredFieldElement 對象,並添加到事先聲明好的currElements集合中。需要注意的是,這裏會跳過靜態字段,因此靜態字段是無法通過這幾個註解進行注入的。

ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod); if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (Modifier.isStatic(method.getModifiers())) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation is not supported on static methods: " + method); } return; } if (method.getParameterCount() == 0) { if (logger.isInfoEnabled()) { logger.info("Autowired annotation should only be used on methods with parameters: " + method); } } boolean required = determineRequiredStatus(ann); PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new AutowiredMethodElement(method, required, pd)); } });

接着,再將添加了這些註解的非晶態方法,封裝成 AutowiredMethodElement 對象,添加到currElements集合中。

elements.addAll(0, currElements); targetClass = targetClass.getSuperclass();

在循環體的最後,將currElements中的內容都添加到elements集合中,然後將targetClass指向其父類。

這裏結合循環體的while條件,可以知道,這個循環體要執行的工作是,以方法參數傳入的類型開始遍歷它的父類,一直到 Object 類之前,將這些類中聲明的所有帶注入相關注解的非靜態的屬性和方法都封裝成 InjectionMetadata.InjectedElement 對象(屬性對應 AutowiredFieldElement,方法對應 AutowiredMethodElement),然後統一放到elements集合中。

return InjectionMetadata.forElements(elements, clazz);

方法的最後,將elements集合和類型信息封裝成 InjectionMetadata 返回。

checkConfigMembers

回到postProcessMergedBeanDefinition方法,在得到 InjectionMetadata 類型的註解信息metadata後,會調用它的checkConfigMembers方法。

// org.springframework.beans.factory.annotation.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); if (logger.isTraceEnabled()) { logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element); } } } this.checkedElements = checkedElements; }

這個方法中,會對injectedElements成員變量集合進行遍歷,這裏 InjectionMetadata 的injectedElements的成員變量,其實就是上一部中創建 InjectionMetadata 對象時,傳入的elements參數,也就是解析出的帶注入註解的屬性和方法封裝後的對象集合。

循環中會判斷當前遍歷到的element,在 BeanDefinition 中是不是外部管理的配置成員,這裏的成員指的就是屬性或者方法,如果不是的話,則將其註冊為外部管理的配置成員,也就是添加到 BeanDefinition 的externallyManagedConfigMembers集合中,然後再將element對象添加到事先聲明好的 InjectedElement 集合checkedElements中。

方法的最後,將checkedElements集合賦值給 InjectionMetadata 的checkedElements成員變量。

總結

本文介紹了 AutowiredAnnotationBeanPostProcessor 後處理器中的postProcessMergedBeanDefinition方法,它的作用是在當前處理的類型中解析出需要注入的屬性和方法。

下一篇講分析 AutowiredAnnotationBeanPostProcessor 最後一個比較重要的處理方法postProcessProperties