最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解@Autowired是如何注入變量的

 更新時(shí)間:2023年07月20日 09:58:33   作者:江南一點(diǎn)雨  
在?Spring?容器中,當(dāng)我們想給某一個(gè)屬性注入值的時(shí)候,有多種不同的方式,例如使用?@Autowired、@Inject等注解,下面小編就來(lái)和小伙伴們聊一聊,@Autowired?到底是如何把數(shù)據(jù)注入進(jìn)來(lái)的

在 Spring 容器中,當(dāng)我們想給某一個(gè)屬性注入值的時(shí)候,有多種不同的方式,例如可以通過(guò)構(gòu)造器注入、可以通過(guò) set 方法注入,也可以使用 @Autowired、@Inject、@Resource 等注解注入。

今天松哥就來(lái)和小伙伴們聊一聊,@Autowired 到底是如何把數(shù)據(jù)注入進(jìn)來(lái)的。

@Service
public?class?AService?{
????@Autowired
????BService?bService;
}

1. Bean 的創(chuàng)建

這個(gè)問(wèn)題我們就得從 Bean 的創(chuàng)建開(kāi)始了,本文主要是和小伙伴們聊 @Autowired,所以 Bean 的創(chuàng)建我就不從第一步開(kāi)始了,咱們直接來(lái)看關(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;
}

在這個(gè)方法中,首先會(huì)創(chuàng)建原始的 Bean 對(duì)象,創(chuàng)建出來(lái)之后,會(huì)調(diào)用一個(gè) populateBean 方法,這個(gè)方法就是給 Bean 的各個(gè)屬性賦值的方法,標(biāo)注了 @Autowired 注解的屬性被自動(dòng)賦值也是在這個(gè)方法中完成的。

2. populateBean

populateBean 方法內(nèi)容比較多,我們來(lá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);
?}
}

這里松哥貼出來(lái)的是部分關(guān)鍵代碼。

首先來(lái)看上面有一個(gè) if,這個(gè) if 主要是判斷是否需要后置處理器進(jìn)行處理,如果不需要,那么就直接 return 掉了,默認(rèn)情況下,這里并不會(huì) return 掉,而是會(huì)繼續(xù)走后面的流程,因?yàn)?postProcessAfterInstantiation 方法默認(rèn)返回 true。

接下來(lái)第二個(gè) if 就是比較關(guān)鍵的一個(gè)地方了,在這里會(huì)遍歷所有相關(guān)的后置處理器,嘗試通過(guò)這些處理器去獲取到需要的 value。

負(fù)責(zé)處理 @Autowired 注解的后置處理器是 AutowiredAnnotationBeanPostProcessor,所以現(xiàn)在,我們就來(lái)到 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;
}

這個(gè)方法其實(shí)就兩步,第一步 findAutowiringMetadata,第二步 inject,就這兩件事。分別來(lái)看。

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;
}

這個(gè)方法會(huì)先嘗試從緩存中獲取 metadata,如果能夠從緩存中獲取到,那就直接返回,緩存中沒(méi)有的話,那么最終會(huì)調(diào)用到 buildAutowiringMetadata 方法,去重新構(gòu)建 metadata,并將構(gòu)建結(jié)果存入到緩存中,以備下一次使用。

那么我們來(lái)看下 metadata 到底是如何構(gòu)建出來(lái)的。

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);
}

這個(gè)方法比較長(zhǎng),我來(lái)和大家說(shuō)一下核心邏輯。

首先會(huì)調(diào)用 isCandidateClass 方法判斷當(dāng)前類是否為一個(gè)候選類,判斷的依據(jù)就是 autowiredAnnotationTypes 變量的值,這個(gè)變量在該類的構(gòu)造方法中進(jìn)行了初始化,大家來(lái)看下這個(gè)構(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 集合中有兩個(gè)注解是固定的:@Autowired 和 @Value,另外就是如果項(xiàng)目引入了 JSR-330 依賴,則 @Inject 注解也會(huì)被加入進(jìn)來(lái),以前 @Inject 存在于 javax 包中,現(xiàn)在最新版 @Inject 注解存在于 jakarta 包中,這里把兩種情況都列出來(lái)了。

所以,isCandidateClass 方法實(shí)際上就是判斷當(dāng)前類在類、屬性、方法等層面上是否存在上述三個(gè)注解,如果存在,則就是候選類,否則就不是候選類。如果不是候選類則返回一個(gè)空的 InjectionMetadata 對(duì)象,否則就繼續(xù)后面的流程。

后面的流程,首先是一個(gè) do{}while() 結(jié)構(gòu),通過(guò)這個(gè)循環(huán)把當(dāng)前類以及當(dāng)前類的父類中的滿足條件的注解都找出來(lái)。具體的找法就是首先調(diào)用 ReflectionUtils.doWithLocalFields 方法,這個(gè)方法會(huì)遍歷當(dāng)前類的所有屬性,找到那些包含了 autowiredAnnotationTypes 中定義的注解的屬性,并將之封裝為 AutowiredFieldElement 對(duì)象,然后存入到集合中,接下來(lái)就是調(diào)用 ReflectionUtils.doWithLocalMethods,這個(gè)是找到當(dāng)前類中包含了上述三個(gè)注解的方法,然后把找到的滿足條件的方法封裝為 AutowiredMethodElement 然后存入到集合中。

另外大家需要注意,無(wú)論是 AutowiredFieldElement 還是 AutowiredMethodElement,都是 InjectionMetadata.InjectedElement 的子類。

這就是 findAutowiringMetadata 方法所做的事情,整體上來(lái)看,就是查找到添加了 @Autowired 或者 @Value 或者 @Inject 注解的屬性或者方法,并將之存入到集合中。

3.2 inject

接下來(lái)就該調(diào)用 metadata.inject 了,我們來(lái)看下該方法:

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,然后挨個(gè)調(diào)用其 inject 方法進(jìn)行屬性注入。以本文一開(kāi)始的 demo 為例,@Autowired 注解加在屬性上面,所以我們這里實(shí)際上調(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);
?}
}

這段代碼首先會(huì)調(diào)用 resolvedCachedArgument 方法嘗試從緩存中獲取想要的對(duì)象,如果緩存中存在,則可以直接使用,如果緩存中沒(méi)有,則調(diào)用 resolveFieldValue 方法去獲取。獲取到之后,通過(guò)反射調(diào)用 set 方法進(jìn)行賦值就可以了。所以關(guān)鍵步驟又來(lái)到了 resolveFieldValue 方法中。

用緩存的好處就是,獲取到對(duì)象存入到緩存之后,如果相同的 Bean 在多個(gè)類中注入,那么只有第一次需要去加載,以后就直接用緩存中的數(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;
}

這個(gè)方法的核心其實(shí)就是通過(guò) beanFactory.resolveDependency 方法獲取到想要的 Bean 對(duì)象,我們直接來(lái)看這個(gè)核心方法,由于 BeanFactory 是一個(gè)接口,所以這個(gè)方法的實(shí)現(xiàn)實(shí)際上是在 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;
?}
}

這里一共是四個(gè)分支,處理四種不同的情況,分別是 Optional、ObjectFactory、JSR-330 以及其他情況,很明顯,文章開(kāi)頭的案例應(yīng)該屬于第四種情況,我們繼續(xù)來(lái)看 doResolveDependency 方法。

3.3 doResolveDependency

這個(gè)方法也是比較長(zhǎng),我列出來(lái)了一些關(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 方法去查找多個(gè) Bean,這是因?yàn)槲覀冊(cè)谧⑷氲臅r(shí)候,可以注入數(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;
?}
}

這里會(huì)首先判斷你的數(shù)據(jù)類型,針對(duì) Stream、數(shù)組、集合 以及 Map 分別處理,處理代碼都很好懂,以集合為例,首先獲取到集合中的泛型,然后調(diào)用 findAutowireCandidates 方法根據(jù)泛型去查找到 Bean,處理一下返回就行了,其他幾種數(shù)據(jù)類型也都差不多。

至于 findAutowireCandidates 方法的邏輯,我們就不去細(xì)看了,我大概和小伙伴們說(shuō)一下,就是先根據(jù) Bean 的類型,調(diào)用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法去當(dāng)前容器連同父容器中,查找到所有滿足條件的 Bean,處理之后返回。

接下來(lái)回到本小節(jié)一開(kāi)始的源碼中,處理完集合之后,接下來(lái)也是調(diào)用 findAutowireCandidates 方法去查找滿足條件的 Bean,但是這個(gè)方法查找出來(lái)的 Bean 可能有多個(gè),如果存在多個(gè),則要通過(guò) @Primary 注解或者其他優(yōu)先級(jí)順序,去確定到底使用哪一個(gè)(執(zhí)行 determineAutowireCandidate 方法),如果查找到一個(gè) Bean,那就把找到的 Bean 返回即可。

這就是 @Autowired 一個(gè)完整的解析過(guò)程。

4. 時(shí)序圖

最后,結(jié)合如下時(shí)序圖,我再和小伙伴們梳理一下上面的過(guò)程。

1.在創(chuàng)建 Bean 的時(shí)候,原始 Bean 創(chuàng)建出來(lái)之后,會(huì)調(diào)用 populateBean 方法進(jìn)行 Bean 的屬性填充。

2.接下來(lái)調(diào)用 postProcessAfterInstantiation 方法去判斷是否需要執(zhí)行后置處理器,如果不需要,就直接返回了。

3.調(diào)用 postProcessProperties 方法,去觸發(fā)各種后置處理器的執(zhí)行。

4.在第 3 步的方法中,調(diào)用 findAutowiringMetadata,這個(gè)方法又會(huì)進(jìn)一步觸發(fā) buildAutorwiringMetadata 方法,去找到包含了 @Autowired、@Value 以及 @Inject 注解的屬性或者方法,并將之封裝為 InjectedElement 返回。

5.調(diào)用 InjectedElement#inject 方法進(jìn)行屬性注入。

6.接下來(lái)執(zhí)行 resolvedCachedArgument 方法嘗試從緩存中找到需要的 Bean 對(duì)象。

7.如果緩存中不存在,則調(diào)用 resolveFieldValue 方法去容器中找到 Bean。

8.最后調(diào)用 makeAccessible 和 set 方法完成屬性的賦值。

以上就是詳解@Autowired是如何注入變量的的詳細(xì)內(nèi)容,更多關(guān)于@Autowired注入變量的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java如何獲取主機(jī)的基本信息詳解

    Java如何獲取主機(jī)的基本信息詳解

    最近遇到一個(gè)工作需求,上網(wǎng)查了一下怎樣在Java中獲取本機(jī)的ip和主機(jī)名,所以下面這篇文章主要給大家介紹了關(guān)于Java如何獲取主機(jī)的基本信息,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-12-12
  • MyBatisPlus枚舉處理器的實(shí)現(xiàn)

    MyBatisPlus枚舉處理器的實(shí)現(xiàn)

    MyBatis-Plus?提供了枚舉處理器,讓我們可以優(yōu)雅地在數(shù)據(jù)庫(kù)字段和Java?枚舉之間進(jìn)行轉(zhuǎn)換,而不需要手動(dòng)處理映射關(guān)系,下面就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下
    2026-03-03
  • Java字符串split方法的坑及解決

    Java字符串split方法的坑及解決

    這篇文章主要介紹了Java字符串split方法的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Java程序中方法的用法重載和遞歸

    Java程序中方法的用法重載和遞歸

    Java語(yǔ)言中的“方法”在其他語(yǔ)言當(dāng)中也可能被稱為“函數(shù)”(Function)。對(duì)于一些復(fù)雜的代碼邏輯,如果希望重復(fù)使用這些代碼,并且做到“隨時(shí)任意使用”,那么就可以將這些代碼放在一個(gè)大括號(hào)“{}”當(dāng)中,并且起一個(gè)名字。使用代碼的時(shí)候,直接找到名字調(diào)用即可
    2021-10-10
  • 簡(jiǎn)單了解SpringMVC常用組件作用解析

    簡(jiǎn)單了解SpringMVC常用組件作用解析

    這篇文章主要介紹了簡(jiǎn)單了解SpringMVC常用組件作用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 基于自定義BufferedReader中的read和readLine方法

    基于自定義BufferedReader中的read和readLine方法

    下面小編就為大家分享一篇基于自定義BufferedReader中的read和readLine方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • java實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)后綴的方法

    java實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)后綴的方法

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)中綴表達(dá)式轉(zhuǎn)后綴的表達(dá)式方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • mybatis攔截器及不生效的解決方法

    mybatis攔截器及不生效的解決方法

    本文主要介紹了mybatis攔截器及不生效的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • java實(shí)現(xiàn)輸入輸出流代碼分享

    java實(shí)現(xiàn)輸入輸出流代碼分享

    這篇文章主要介紹了java實(shí)現(xiàn)輸入輸出流代碼分享的相關(guān)資料,需要的朋友可以參考下
    2015-09-09
  • SpringBoot源碼分析之bootstrap.properties文件加載的原理

    SpringBoot源碼分析之bootstrap.properties文件加載的原理

    本文通過(guò)訪問(wèn)看到bootstrap.properties中的信息獲取到了,同時(shí)age也被application.properties中的屬性覆蓋掉了。加載順序到底是什么?為什么會(huì)覆蓋呢?我們接下來(lái)分析下吧
    2021-12-12

最新評(píng)論

罗山县| 罗源县| 佛山市| 昌都县| 贵溪市| 工布江达县| 桑植县| 桃园市| 宁乡县| 涞源县| 柳江县| 区。| 彭泽县| 岳池县| 呼和浩特市| 丹寨县| 南华县| 长汀县| 铜梁县| 阿坝县| 柘荣县| 从化市| 沐川县| 平乐县| 泾阳县| 东乡县| 文山县| 天柱县| 冀州市| 曲靖市| 东莞市| 鄂尔多斯市| 汝南县| 临颍县| 嘉义市| 昭觉县| 慈溪市| 穆棱市| 大足县| 泸定县| 承德市|