詳解@Autowired是如何注入變量的
在 Spring 容器中,當我們想給某一個屬性注入值的時候,有多種不同的方式,例如可以通過構(gòu)造器注入、可以通過 set 方法注入,也可以使用 @Autowired、@Inject、@Resource 等注解注入。
今天松哥就來和小伙伴們聊一聊,@Autowired 到底是如何把數(shù)據(jù)注入進來的。
@Service public?class?AService?{ ????@Autowired ????BService?bService; }
1. Bean 的創(chuàng)建
這個問題我們就得從 Bean 的創(chuàng)建開始了,本文主要是和小伙伴們聊 @Autowired,所以 Bean 的創(chuàng)建我就不從第一步開始了,咱們直接來看關(guān)鍵的方法,那就是 AbstractAutowireCapableBeanFactory#doCreateBean 方法:
protected?Object?doCreateBean(String?beanName,?RootBeanDefinition?mbd,?@Nullable?Object[]?args) ??throws?BeanCreationException?{ ????//.... ?Object?exposedObject?=?bean; ?try?{ ??populateBean(beanName,?mbd,?instanceWrapper); ??exposedObject?=?initializeBean(beanName,?exposedObject,?mbd); ?} ?//... ?return?exposedObject; }
在這個方法中,首先會創(chuàng)建原始的 Bean 對象,創(chuàng)建出來之后,會調(diào)用一個 populateBean 方法,這個方法就是給 Bean 的各個屬性賦值的方法,標注了 @Autowired 注解的屬性被自動賦值也是在這個方法中完成的。
2. populateBean
populateBean 方法內(nèi)容比較多,我們來看一些關(guān)鍵的地方:
protected?void?populateBean(String?beanName,?RootBeanDefinition?mbd,?@Nullable?BeanWrapper?bw)?{ ?//?Give?any?InstantiationAwareBeanPostProcessors?the?opportunity?to?modify?the ?//?state?of?the?bean?before?properties?are?set.?This?can?be?used,?for?example, ?//?to?support?styles?of?field?injection. ?if?(!mbd.isSynthetic()?&&?hasInstantiationAwareBeanPostProcessors())?{ ??for?(InstantiationAwareBeanPostProcessor?bp?:?getBeanPostProcessorCache().instantiationAware)?{ ???if?(!bp.postProcessAfterInstantiation(bw.getWrappedInstance(),?beanName))?{ ????return; ???} ??} ?} ?//... ?if?(hasInstantiationAwareBeanPostProcessors())?{ ??if?(pvs?==?null)?{ ???pvs?=?mbd.getPropertyValues(); ??} ??for?(InstantiationAwareBeanPostProcessor?bp?:?getBeanPostProcessorCache().instantiationAware)?{ ???PropertyValues?pvsToUse?=?bp.postProcessProperties(pvs,?bw.getWrappedInstance(),?beanName); ???if?(pvsToUse?==?null)?{ ????return; ???} ???pvs?=?pvsToUse; ??} ?} ?boolean?needsDepCheck?=?(mbd.getDependencyCheck()?!=?AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); ?if?(needsDepCheck)?{ ??PropertyDescriptor[]?filteredPds?=?filterPropertyDescriptorsForDependencyCheck(bw,?mbd.allowCaching); ??checkDependencies(beanName,?mbd,?filteredPds,?pvs); ?} ?if?(pvs?!=?null)?{ ??applyPropertyValues(beanName,?mbd,?bw,?pvs); ?} }
這里松哥貼出來的是部分關(guān)鍵代碼。
首先來看上面有一個 if,這個 if 主要是判斷是否需要后置處理器進行處理,如果不需要,那么就直接 return 掉了,默認情況下,這里并不會 return 掉,而是會繼續(xù)走后面的流程,因為 postProcessAfterInstantiation 方法默認返回 true。
接下來第二個 if 就是比較關(guān)鍵的一個地方了,在這里會遍歷所有相關(guān)的后置處理器,嘗試通過這些處理器去獲取到需要的 value。
負責處理 @Autowired 注解的后置處理器是 AutowiredAnnotationBeanPostProcessor,所以現(xiàn)在,我們就來到 AutowiredAnnotationBeanPostProcessor#postProcessProperties 方法了。
3. postProcessProperties
@Override public?PropertyValues?postProcessProperties(PropertyValues?pvs,?Object?bean,?String?beanName)?{ ?InjectionMetadata?metadata?=?findAutowiringMetadata(beanName,?bean.getClass(),?pvs); ?try?{ ??metadata.inject(bean,?beanName,?pvs); ?} ?catch?(BeanCreationException?ex)?{ ??throw?ex; ?} ?catch?(Throwable?ex)?{ ??throw?new?BeanCreationException(beanName,?"Injection?of?autowired?dependencies?failed",?ex); ?} ?return?pvs; }
這個方法其實就兩步,第一步 findAutowiringMetadata,第二步 inject,就這兩件事。分別來看。
3.1 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; }
這個方法會先嘗試從緩存中獲取 metadata,如果能夠從緩存中獲取到,那就直接返回,緩存中沒有的話,那么最終會調(diào)用到 buildAutowiringMetadata 方法,去重新構(gòu)建 metadata,并將構(gòu)建結(jié)果存入到緩存中,以備下一次使用。
那么我們來看下 metadata 到底是如何構(gòu)建出來的。
private?InjectionMetadata?buildAutowiringMetadata(Class<?>?clazz)?{ ?if?(!AnnotationUtils.isCandidateClass(clazz,?this.autowiredAnnotationTypes))?{ ??return?InjectionMetadata.EMPTY; ?} ?List<InjectionMetadata.InjectedElement>?elements?=?new?ArrayList<>(); ?Class<?>?targetClass?=?clazz; ?do?{ ??final?List<InjectionMetadata.InjectedElement>?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)); ???} ??}); ??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)); ???} ??}); ??elements.addAll(0,?currElements); ??targetClass?=?targetClass.getSuperclass(); ?} ?while?(targetClass?!=?null?&&?targetClass?!=?Object.class); ?return?InjectionMetadata.forElements(elements,?clazz); }
這個方法比較長,我來和大家說一下核心邏輯。
首先會調(diào)用 isCandidateClass 方法判斷當前類是否為一個候選類,判斷的依據(jù)就是 autowiredAnnotationTypes 變量的值,這個變量在該類的構(gòu)造方法中進行了初始化,大家來看下這個構(gòu)造方法:
public?AutowiredAnnotationBeanPostProcessor()?{ ?this.autowiredAnnotationTypes.add(Autowired.class); ?this.autowiredAnnotationTypes.add(Value.class); ?try?{ ??this.autowiredAnnotationTypes.add((Class<??extends?Annotation>) ????ClassUtils.forName("jakarta.inject.Inject",?AutowiredAnnotationBeanPostProcessor.class.getClassLoader())); ??logger.trace("'jakarta.inject.Inject'?annotation?found?and?supported?for?autowiring"); ?} ?catch?(ClassNotFoundException?ex)?{ ??//?jakarta.inject?API?not?available?-?simply?skip. ?} ?try?{ ??this.autowiredAnnotationTypes.add((Class<??extends?Annotation>) ????ClassUtils.forName("javax.inject.Inject",?AutowiredAnnotationBeanPostProcessor.class.getClassLoader())); ??logger.trace("'javax.inject.Inject'?annotation?found?and?supported?for?autowiring"); ?} ?catch?(ClassNotFoundException?ex)?{ ??//?javax.inject?API?not?available?-?simply?skip. ?} }
小伙伴們看到,autowiredAnnotationTypes 集合中有兩個注解是固定的:@Autowired 和 @Value,另外就是如果項目引入了 JSR-330 依賴,則 @Inject 注解也會被加入進來,以前 @Inject 存在于 javax 包中,現(xiàn)在最新版 @Inject 注解存在于 jakarta 包中,這里把兩種情況都列出來了。
所以,isCandidateClass 方法實際上就是判斷當前類在類、屬性、方法等層面上是否存在上述三個注解,如果存在,則就是候選類,否則就不是候選類。如果不是候選類則返回一個空的 InjectionMetadata 對象,否則就繼續(xù)后面的流程。
后面的流程,首先是一個 do{}while() 結(jié)構(gòu),通過這個循環(huán)把當前類以及當前類的父類中的滿足條件的注解都找出來。具體的找法就是首先調(diào)用 ReflectionUtils.doWithLocalFields 方法,這個方法會遍歷當前類的所有屬性,找到那些包含了 autowiredAnnotationTypes 中定義的注解的屬性,并將之封裝為 AutowiredFieldElement 對象,然后存入到集合中,接下來就是調(diào)用 ReflectionUtils.doWithLocalMethods,這個是找到當前類中包含了上述三個注解的方法,然后把找到的滿足條件的方法封裝為 AutowiredMethodElement 然后存入到集合中。
另外大家需要注意,無論是 AutowiredFieldElement 還是 AutowiredMethodElement,都是 InjectionMetadata.InjectedElement
的子類。
這就是 findAutowiringMetadata 方法所做的事情,整體上來看,就是查找到添加了 @Autowired 或者 @Value 或者 @Inject 注解的屬性或者方法,并將之存入到集合中。
3.2 inject
接下來就該調(diào)用 metadata.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); ??} ?} }
這里就是遍歷剛剛上一步收集到的 InjectedElement,然后挨個調(diào)用其 inject 方法進行屬性注入。以本文一開始的 demo 為例,@Autowired 注解加在屬性上面,所以我們這里實際上調(diào)用的是 AutowiredFieldElement#inject 方法:
@Override protected?void?inject(Object?bean,?@Nullable?String?beanName,?@Nullable?PropertyValues?pvs)?throws?Throwable?{ ?Field?field?=?(Field)?this.member; ?Object?value; ?if?(this.cached)?{ ??try?{ ???value?=?resolvedCachedArgument(beanName,?this.cachedFieldValue); ??} ??catch?(NoSuchBeanDefinitionException?ex)?{ ???//?Unexpected?removal?of?target?bean?for?cached?argument?->?re-resolve ???value?=?resolveFieldValue(field,?bean,?beanName); ??} ?} ?else?{ ??value?=?resolveFieldValue(field,?bean,?beanName); ?} ?if?(value?!=?null)?{ ??ReflectionUtils.makeAccessible(field); ??field.set(bean,?value); ?} }
這段代碼首先會調(diào)用 resolvedCachedArgument 方法嘗試從緩存中獲取想要的對象,如果緩存中存在,則可以直接使用,如果緩存中沒有,則調(diào)用 resolveFieldValue 方法去獲取。獲取到之后,通過反射調(diào)用 set 方法進行賦值就可以了。所以關(guān)鍵步驟又來到了 resolveFieldValue 方法中。
用緩存的好處就是,獲取到對象存入到緩存之后,如果相同的 Bean 在多個類中注入,那么只有第一次需要去加載,以后就直接用緩存中的數(shù)據(jù)即可。
@Nullable private?Object?resolveFieldValue(Field?field,?Object?bean,?@Nullable?String?beanName)?{ ?//... ?Object?value; ?try?{ ??value?=?beanFactory.resolveDependency(desc,?beanName,?autowiredBeanNames,?typeConverter); ?} ????//... ?return?value; }
這個方法的核心其實就是通過 beanFactory.resolveDependency 方法獲取到想要的 Bean 對象,我們直接來看這個核心方法,由于 BeanFactory 是一個接口,所以這個方法的實現(xiàn)實際上是在 DefaultListableBeanFactory#resolveDependency:
@Nullable public?Object?resolveDependency(DependencyDescriptor?descriptor,?@Nullable?String?requestingBeanName, ??@Nullable?Set<String>?autowiredBeanNames,?@Nullable?TypeConverter?typeConverter)?throws?BeansException?{ ?descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); ?if?(Optional.class?==?descriptor.getDependencyType())?{ ??return?createOptionalDependency(descriptor,?requestingBeanName); ?} ?else?if?(ObjectFactory.class?==?descriptor.getDependencyType()?|| ???ObjectProvider.class?==?descriptor.getDependencyType())?{ ??return?new?DependencyObjectProvider(descriptor,?requestingBeanName); ?} ?else?if?(javaxInjectProviderClass?==?descriptor.getDependencyType())?{ ??return?new?Jsr330Factory().createDependencyProvider(descriptor,?requestingBeanName); ?} ?else?{ ??Object?result?=?getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( ????descriptor,?requestingBeanName); ??if?(result?==?null)?{ ???result?=?doResolveDependency(descriptor,?requestingBeanName,?autowiredBeanNames,?typeConverter); ??} ??return?result; ?} }
這里一共是四個分支,處理四種不同的情況,分別是 Optional、ObjectFactory、JSR-330 以及其他情況,很明顯,文章開頭的案例應該屬于第四種情況,我們繼續(xù)來看 doResolveDependency 方法。
3.3 doResolveDependency
這個方法也是比較長,我列出來了一些關(guān)鍵的部分:
@Nullable public?Object?doResolveDependency(DependencyDescriptor?descriptor,?@Nullable?String?beanName, ??@Nullable?Set<String>?autowiredBeanNames,?@Nullable?TypeConverter?typeConverter)?throws?BeansException?{ ????????//... ??Object?multipleBeans?=?resolveMultipleBeans(descriptor,?beanName,?autowiredBeanNames,?typeConverter); ??if?(multipleBeans?!=?null)?{ ???return?multipleBeans; ??} ??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?type,?descriptor); ??if?(matchingBeans.isEmpty())?{ ???if?(isRequired(descriptor))?{ ????raiseNoMatchingBeanFound(type,?descriptor.getResolvableType(),?descriptor); ???} ???return?null; ??} ??String?autowiredBeanName; ??Object?instanceCandidate; ??if?(matchingBeans.size()?>?1)?{ ???autowiredBeanName?=?determineAutowireCandidate(matchingBeans,?descriptor); ???if?(autowiredBeanName?==?null)?{ ????if?(isRequired(descriptor)?||?!indicatesMultipleBeans(type))?{ ?????return?descriptor.resolveNotUnique(descriptor.getResolvableType(),?matchingBeans); ????} ????else?{ ?????//?In?case?of?an?optional?Collection/Map,?silently?ignore?a?non-unique?case: ?????//?possibly?it?was?meant?to?be?an?empty?collection?of?multiple?regular?beans ?????//?(before?4.3?in?particular?when?we?didn't?even?look?for?collection?beans). ?????return?null; ????} ???} ???instanceCandidate?=?matchingBeans.get(autowiredBeanName); ??} ??else?{ ???//?We?have?exactly?one?match. ???Map.Entry<String,?Object>?entry?=?matchingBeans.entrySet().iterator().next(); ???autowiredBeanName?=?entry.getKey(); ???instanceCandidate?=?entry.getValue(); ??} ??Object?result?=?instanceCandidate; ??return?result; ?} ?finally?{ ??ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); ?} }
首先是調(diào)用 resolveMultipleBeans 方法去查找多個 Bean,這是因為我們在注入的時候,可以注入數(shù)組、集合和 Map,例如像下面這樣:
@Service public?class?AService?{ ????@Autowired ????BService?bService; ????@Autowired ????BService[]?bServices; ????@Autowired ????List<BService>?bServiceList; ????@Autowired ????Map<String,?BService>?bServiceMap; }
具體查找方法如下:
@Nullable private?Object?resolveMultipleBeans(DependencyDescriptor?descriptor,?@Nullable?String?beanName, ??@Nullable?Set<String>?autowiredBeanNames,?@Nullable?TypeConverter?typeConverter)?{ ?Class<?>?type?=?descriptor.getDependencyType(); ?if?(descriptor?instanceof?StreamDependencyDescriptor?streamDependencyDescriptor)?{ ??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?type,?descriptor); ??//... ??return?stream; ?} ?else?if?(type.isArray())?{ ??Class<?>?componentType?=?type.getComponentType(); ??ResolvableType?resolvableType?=?descriptor.getResolvableType(); ??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?componentType, ????new?MultiElementDescriptor(descriptor)); ??return?result; ?} ?else?if?(Collection.class.isAssignableFrom(type)?&&?type.isInterface())?{ ??Class<?>?elementType?=?descriptor.getResolvableType().asCollection().resolveGeneric(); ??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?elementType, ????new?MultiElementDescriptor(descriptor)); ??return?result; ?} ?else?if?(Map.class?==?type)?{ ??ResolvableType?mapType?=?descriptor.getResolvableType().asMap(); ??Map<String,?Object>?matchingBeans?=?findAutowireCandidates(beanName,?valueType, ????new?MultiElementDescriptor(descriptor)); ??return?matchingBeans; ?} ?else?{ ??return?null; ?} }
這里會首先判斷你的數(shù)據(jù)類型,針對 Stream、數(shù)組、集合 以及 Map 分別處理,處理代碼都很好懂,以集合為例,首先獲取到集合中的泛型,然后調(diào)用 findAutowireCandidates 方法根據(jù)泛型去查找到 Bean,處理一下返回就行了,其他幾種數(shù)據(jù)類型也都差不多。
至于 findAutowireCandidates 方法的邏輯,我們就不去細看了,我大概和小伙伴們說一下,就是先根據(jù) Bean 的類型,調(diào)用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法去當前容器連同父容器中,查找到所有滿足條件的 Bean,處理之后返回。
接下來回到本小節(jié)一開始的源碼中,處理完集合之后,接下來也是調(diào)用 findAutowireCandidates 方法去查找滿足條件的 Bean,但是這個方法查找出來的 Bean 可能有多個,如果存在多個,則要通過 @Primary 注解或者其他優(yōu)先級順序,去確定到底使用哪一個(執(zhí)行 determineAutowireCandidate 方法),如果查找到一個 Bean,那就把找到的 Bean 返回即可。
這就是 @Autowired 一個完整的解析過程。
4. 時序圖
最后,結(jié)合如下時序圖,我再和小伙伴們梳理一下上面的過程。
1.在創(chuàng)建 Bean 的時候,原始 Bean 創(chuàng)建出來之后,會調(diào)用 populateBean 方法進行 Bean 的屬性填充。
2.接下來調(diào)用 postProcessAfterInstantiation 方法去判斷是否需要執(zhí)行后置處理器,如果不需要,就直接返回了。
3.調(diào)用 postProcessProperties 方法,去觸發(fā)各種后置處理器的執(zhí)行。
4.在第 3 步的方法中,調(diào)用 findAutowiringMetadata,這個方法又會進一步觸發(fā) buildAutorwiringMetadata 方法,去找到包含了 @Autowired、@Value 以及 @Inject 注解的屬性或者方法,并將之封裝為 InjectedElement 返回。
5.調(diào)用 InjectedElement#inject 方法進行屬性注入。
6.接下來執(zhí)行 resolvedCachedArgument 方法嘗試從緩存中找到需要的 Bean 對象。
7.如果緩存中不存在,則調(diào)用 resolveFieldValue 方法去容器中找到 Bean。
8.最后調(diào)用 makeAccessible 和 set 方法完成屬性的賦值。
以上就是詳解@Autowired是如何注入變量的的詳細內(nèi)容,更多關(guān)于@Autowired注入變量的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring-boot-maven-plugin引入出現(xiàn)爆紅(已解決)
這篇文章主要介紹了spring-boot-maven-plugin引入出現(xiàn)爆紅(已解決),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-03-03IDEA在plugins里搜不到mybatisx插件的解決方法
本文主要介紹了IDEA在plugins里搜不到mybatisx插件的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-06-06Spring?Cloud?Gateway中netty線程池優(yōu)化示例詳解
這篇文章主要介紹了Spring?Cloud?Gateway中netty線程池優(yōu)化示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07使用Spring Security集成手機驗證碼登錄功能實現(xiàn)
本文詳細介紹了如何利用SpringSecurity來實現(xiàn)手機驗證碼的注冊和登錄功能,在登錄過程中,同樣需通過驗證碼進行驗證,文章還提供了相關(guān)的代碼實現(xiàn)2024-10-10