@Autowired 到底是怎么把變量注入進(jìn)來的?
在 Spring 容器中,當(dāng)我們想給某一個屬性注入值的時候,有多種不同的方式,例如可以通過構(gòu)造器注入、可以通過 set 方法注入,也可以使用 @Autowired、@Inject、@Resource 等注解注入。
今天松哥就來和小伙伴們聊一聊,@Autowired 到底是如何把數(shù)據(jù)注入進(jìn)來的。
@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 的各個屬性賦值的方法,標(biāo)注了 @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 主要是判斷是否需要后置處理器進(jìn)行處理,如果不需要,那么就直接 return 掉了,默認(rèn)情況下,這里并不會 return 掉,而是會繼續(xù)走后面的流程,因為 postProcessAfterInstantiation 方法默認(rèn)返回 true。
接下來第二個 if 就是比較關(guān)鍵的一個地方了,在這里會遍歷所有相關(guān)的后置處理器,嘗試通過這些處理器去獲取到需要的 value。
負(fù)責(zé)處理 @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 方法判斷當(dāng)前類是否為一個候選類,判斷的依據(jù)就是 autowiredAnnotationTypes 變量的值,這個變量在該類的構(gòu)造方法中進(jìn)行了初始化,大家來看下這個構(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 注解也會被加入進(jìn)來,以前 @Inject 存在于 javax 包中,現(xiàn)在最新版 @Inject 注解存在于 jakarta 包中,這里把兩種情況都列出來了。
所以,isCandidateClass 方法實際上就是判斷當(dāng)前類在類、屬性、方法等層面上是否存在上述三個注解,如果存在,則就是候選類,否則就不是候選類。如果不是候選類則返回一個空的 InjectionMetadata 對象,否則就繼續(xù)后面的流程。
后面的流程,首先是一個 do{}while() 結(jié)構(gòu),通過這個循環(huán)把當(dāng)前類以及當(dāng)前類的父類中的滿足條件的注解都找出來。具體的找法就是首先調(diào)用 ReflectionUtils.doWithLocalFields 方法,這個方法會遍歷當(dāng)前類的所有屬性,找到那些包含了 autowiredAnnotationTypes 中定義的注解的屬性,并將之封裝為 AutowiredFieldElement 對象,然后存入到集合中,接下來就是調(diào)用 ReflectionUtils.doWithLocalMethods,這個是找到當(dāng)前類中包含了上述三個注解的方法,然后把找到的滿足條件的方法封裝為 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 方法進(jìn)行屬性注入。以本文一開始的 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 方法進(jìn)行賦值就可以了。所以關(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 以及其他情況,很明顯,文章開頭的案例應(yīng)該屬于第四種情況,我們繼續(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 方法的邏輯,我們就不去細(xì)看了,我大概和小伙伴們說一下,就是先根據(jù) Bean 的類型,調(diào)用 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法去當(dāng)前容器連同父容器中,查找到所有滿足條件的 Bean,處理之后返回。
接下來回到本小節(jié)一開始的源碼中,處理完集合之后,接下來也是調(diào)用 findAutowireCandidates 方法去查找滿足條件的 Bean,但是這個方法查找出來的 Bean 可能有多個,如果存在多個,則要通過 @Primary 注解或者其他優(yōu)先級順序,去確定到底使用哪一個(執(zhí)行 determineAutowireCandidate 方法),如果查找到一個 Bean,那就把找到的 Bean 返回即可。
這就是 @Autowired 一個完整的解析過程。
4. 時序圖
最后,結(jié)合如下時序圖,我再和小伙伴們梳理一下上面的過程。
圖片
- 在創(chuàng)建 Bean 的時候,原始 Bean 創(chuàng)建出來之后,會調(diào)用 populateBean 方法進(jìn)行 Bean 的屬性填充。
- 接下來調(diào)用 postProcessAfterInstantiation 方法去判斷是否需要執(zhí)行后置處理器,如果不需要,就直接返回了。
- 調(diào)用 postProcessProperties 方法,去觸發(fā)各種后置處理器的執(zhí)行。
圖片
- 在第 3 步的方法中,調(diào)用 findAutowiringMetadata,這個方法又會進(jìn)一步觸發(fā) buildAutorwiringMetadata 方法,去找到包含了 @Autowired、@Value 以及 @Inject 注解的屬性或者方法,并將之封裝為 InjectedElement 返回。
- 調(diào)用 InjectedElement#inject 方法進(jìn)行屬性注入。
圖片
- 接下來執(zhí)行 resolvedCachedArgument 方法嘗試從緩存中找到需要的 Bean 對象。
- 如果緩存中不存在,則調(diào)用 resolveFieldValue 方法去容器中找到 Bean。
- 最后調(diào)用 makeAccessible 和 set 方法完成屬性的賦值。
整體上的流程就是這樣,細(xì)節(jié)小伙伴們參考第二小節(jié)內(nèi)容。
本文轉(zhuǎn)載自微信公眾號「江南一點雨 」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系江南一點雨公眾號。