Spring 中@Autowired,@Resource,@Inject 注解實現原理

使用案例

前置條件: 現在有一個 Vehicle 接口,它有兩個實現類 BusCar ,現在還有一個類 VehicleService 需要注入一個 Vehicle 類型的 Bean:

public interface Vehicle {}@Component
public class Car implements Vehicle {}@Component 
public class Bus implements Vehicle {}

使用 @Autowired 注解注入 Bean

@Autowired 注解可以和 @Qualifier 注解一起使用,在有多個符合條件的 Bean 的情況下限制注入特定名稱的 Bean:

@Component
public class VehicleService {@Autowired@Qualifier("car") //假設這里是想要注入Bean名稱為car的這個Beanprivate Vehicle vehicle;
}

使用 @Inject 注解注入 Bean

@Inject 注解可以和 @Qualifier或者 @Named 注解一起使用,在有多個符合條件的 Bean 的情況下限制注入特定名稱的 Bean:

@Component
public class VehicleService {@Inject@Qualifier("car") //假設這里是想要注入Bean名稱為car的這個Beanprivate Vehicle vehicle;@Inject@Named("bus") //假設這里是想要注入Bean名稱為bus的這個Beanprivate Vehicle anotherVehicle;
}

使用 @Resource 注解注入 Bean:

@Component
public class VehicleService {@Resource(name = "car")private Vehicle vehicle;
}

雖然以上三種使用方法都能夠實現注入 Bean 的需求,但是它們在底層實現上有什么區別呢?

注解體系

在 Java EE 和 Spring 體系中定義了幾套注解:

JSR 250:定義了 @PostConstruct@PreDestroy@Resource 注解,其中 @Resource 注解默認是按照名稱進行注入

JSR 330:定義了 @Inject@Qualifier, @Named 注解,其中 @Inject 注解默認是按照類型進行注入,可以搭配 @Qualifier 或者@Named 注解實現按照名稱注入。

Spring:定義了 @Autowired@Qualifier注解,其中 @Autowired 注解默認是按照類型進行注入,可以搭配 @Qualifier 注解實現按照名稱注入。

當前 JSR 250 定義的注解屬于 jakarta.annotation-api,而 JSR 330 定義的注解屬于 jakarta.inject-api

實現原理

InstantiationAwareBeanPostProcessor 方法調用觸發的位置:

Spring 中提供了 InstantiationAwareBeanPostProcessor 接口,它有一個 postProcessProperties() 負責實現對 Bean 的屬性進行處理。

Spring 中提供了實現類 CommonAnnotationBeanPostProcessor 負責處理 @Resource 注解;提供了實現類 AutowiredAnnotationBeanPostProcessor 負責處理 @Autowired 注解和 @Inject 注解。

InstantiationAwareBeanPostProcessorpostProcessProperties() 方法是在 AbstractAutowireCapableBeanFactory 中的 doCreateBean() 創建 Bean 的方法中觸發調用的,在這個方法中的主要實現邏輯是實例化 Bean -> 填充 Bean 屬性 -> 初始化 Bean。 代碼如下:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {//實例化Bean對象instanceWrapper = createBeanInstance(beanName, mbd, args);}Object bean = instanceWrapper.getWrappedInstance();boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}Object exposedObject = bean;try {//填充Bean屬性populateBean(beanName, mbd, instanceWrapper);//初始化BeanexposedObject = initializeBean(beanName, exposedObject, mbd);}
}

在填充 Bean 屬性的方法 populateBean() 中實現了對 postProcessProperties() 方法的調用,在該方法實現對注解修飾的需要注入的字段進行賦值,即自動注入。 代碼如下:

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {  //省略部分代碼PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);  if (hasInstantiationAwareBeanPostProcessors()) {  if (pvs == null) {  pvs = mbd.getPropertyValues();  }  //這里獲取所有InstantiationAwareBeanPostProcessor接口的實現類for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {  //調用postProcessProperties()方法PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);  if (pvsToUse == null) {  return;  }  pvs = pvsToUse;  }  }  
}

InstantiationAwareBeanPostProcessor 注冊的時機:

既然 InstantiationAwareBeanPostProcessor 是負責處理 Bean 的屬性的自動注入的,那么它一定是在業務 Bean 創建之前就已經完成初始化了,這樣在業務 Bean 創建的時候才能調用它的實例方法。它的初始化是在 Spring 上下文的基類 AbstractApplicationContextrefresh() 方法中完成的。代碼如下:

public void refresh() throws BeansException, IllegalStateException {//省略其它代碼//這里注冊了InstantiationAwareBeanPostProcessorregisterBeanPostProcessors(beanFactory);//省略其它代碼//這里創建所有的單例BeanfinishBeanFactoryInitialization(beanFactory);finishRefresh();
}

而在 registerBeanPostProcessors() 方法中又調用了 PostProcessorRegistrationDelegateregisterBeanPostProcessors() 方法來完成注冊的。代碼如下:

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

PostProcessorRegistrationDelegateregisterBeanPostProcessors() 方法真正實現注冊邏輯。代碼如下:

public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {//這里獲取到所有實現了BeanPostProcessor接口的Bean名稱//InstantiationAwareBeanPostProcessor接口繼承了BeanPostProcessor接口String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);//遍歷Bean名稱調用BeanFactory.getBean()方法觸發BeanPostProcessor Bean的創建//然后根據是否實現了PriorityOrdered接口、Ordered接口和其它分為三大類//分別將這三大類的BeanPostProcessor實例進行注冊List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();List<String> orderedPostProcessorNames = new ArrayList<>();List<String> nonOrderedPostProcessorNames = new ArrayList<>();for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {//這里調用BeanFactory.getBean()方法觸發BeanPostProcessor Bean的創建BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);priorityOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}//首先注冊實現了PriorityOrdered接口的BeanPostProcessorsortPostProcessors(priorityOrderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);//然后觸發實現了Ordered接口的BeanPostProcessor Bean的創建并注冊List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());for (String ppName : orderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);orderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}sortPostProcessors(orderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, orderedPostProcessors);//最后觸發其它BeanPostProcessor Bean的創建并注冊List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());for (String ppName : nonOrderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);nonOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);sortPostProcessors(internalPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, internalPostProcessors);
}

CommonAnnotationBeanPostProcessor 實現邏輯(以修飾字段為例)

首先在 CommonAnnotationBeanPostProcessor 的靜態初始化塊中初始化了它要處理的注解。代碼如下:

static {//這里是為了適配不同版本@Resource注解在不同的包路徑下jakartaResourceType = loadAnnotationType("jakarta.annotation.Resource");if (jakartaResourceType != null) {resourceAnnotationTypes.add(jakartaResourceType);}//這里是為了適配不同版本@Resource注解在不同的包路徑下javaxResourceType = loadAnnotationType("javax.annotation.Resource");if (javaxResourceType != null) {resourceAnnotationTypes.add(javaxResourceType);}
}

在它的 postProcessProperties() 方法中主要實現邏輯為找到 @Resource 注解修飾的字段 -> 通過反射給字段賦值。代碼如下:

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {//找@Resource注解修飾的字段InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);try {//給字段賦值metadata.inject(bean, beanName, pvs);}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);}return pvs;
}

@Resource 注解修飾的字段是在 findResourceMetadata() 方法中實現的,在該方法中又調用了 buildResourceMetadata() 來進行實際的查找,在這個方法中通過反射的方式遍歷字段看它是否有 @Resource 注解修飾,如果是的話把它包裝為一個 ResourceElement 對象放到列表中。最后基于列表構造一個 InjectionMetadata 對象返回。代碼如下:

private InjectionMetadata findResourceMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());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);}//這里調用buildResourceMetadata()方法metadata = buildResourceMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;
}private InjectionMetadata buildResourceMetadata(Class<?> clazz) {List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;//省略部分代碼do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();//這里就會遍歷每個字段看字段是否有@Resource注解修飾有的話就加入到列表中ReflectionUtils.doWithLocalFields(targetClass, field -> {//省略部分代碼if (jakartaResourceType != null && field.isAnnotationPresent(jakartaResourceType)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static fields");}if (!this.ignoredResourceTypes.contains(field.getType().getName())) {currElements.add(new ResourceElement(field, field, null));}}else if (javaxResourceType != null && field.isAnnotationPresent(javaxResourceType)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static fields");}if (!this.ignoredResourceTypes.contains(field.getType().getName())) {currElements.add(new LegacyResourceElement(field, field, null));}}});elements.addAll(0, currElements);targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return InjectionMetadata.forElements(elements, clazz);
}

實際觸發賦值的操作是在 InjectionMetadatainject() 方法中實現的,在它的方法中又會循環調用 InjectedElementinject() 方法。代碼如下:

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

InjectedElementinject() 方法中通過反射的方式將找到的 Bean 賦值給字段。代碼如下:

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)throws Throwable {if (!shouldInject(pvs)) {return;}if (this.isField) {Field field = (Field) this.member;ReflectionUtils.makeAccessible(field);//這里通過反射的方式設置值,設置的值就是根據Bean名稱獲取到的Beanfield.set(target, getResourceToInject(target, requestingBeanName));} else {//省略其它代碼}
}

ResourceElementgetResourceToInject() 方法中實現了查找邏輯:如果 BeanFactory 中包含這個 Bean 名稱對應的 Bean 則直接根據名稱查找,否則會根據類型進行匹配,這個就是常說的 @Resource 注解默認是按照名稱進行匹配的,名稱匹配不到的情況下再按照類型進行匹配。代碼如下:

protected Object getResource(LookupElement element, @Nullable String requestingBeanName)throws NoSuchBeanDefinitionException {//省略代碼// Regular resource autowiringif (this.resourceFactory == null) {throw new NoSuchBeanDefinitionException(element.lookupType,"No resource factory configured - specify the 'resourceFactory' property");}return autowireResource(this.resourceFactory, element, requestingBeanName);
}protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)throws NoSuchBeanDefinitionException {Object resource;Set<String> autowiredBeanNames;String name = element.name;if (factory instanceof AutowireCapableBeanFactory autowireCapableBeanFactory) {//如果根據Bean名稱找不到Bean且允許按照類型匹配的情況下走第一個分支if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {autowiredBeanNames = new LinkedHashSet<>();resource = autowireCapableBeanFactory.resolveDependency(element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);if (resource == null) {throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");}} else { //如果根據名稱找得到Bean則直接根據名稱獲取Beanresource = autowireCapableBeanFactory.resolveBeanByName(name, element.getDependencyDescriptor());autowiredBeanNames = Collections.singleton(name);}} else {//省略代碼}//省略代碼return resource;
}

按照類型匹配的邏輯是在 DefaultListableBeanFactorydoResolveDependency() 方法中實現的,在該方法中會根據類型找到所有是當前類型的 Bean,然后構造一個 Map,key 是 Bean 的名稱,value 是對應的 Bean 對象,如果找到的 Bean 個數大于 1 則會選擇一個最符合條件的返回(選擇的依據后面會講到),如果等于 1 則直接返回這個 Bean。代碼如下:

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);try {//省略代碼//這里根據類型找到所有的Bean,然后Bean的名稱作為key,Bean作為ValueMap<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);if (matchingBeans.isEmpty()) {// Step 4c (fallback): custom Collection / Map declarations for collecting multiple beansmultipleBeans = resolveMultipleBeansFallback(descriptor, beanName, autowiredBeanNames, typeConverter);if (multipleBeans != null) {return multipleBeans;}// Raise exception if nothing found for required injection pointif (isRequired(descriptor)) {raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);}return null;}String autowiredBeanName;Object instanceCandidate;//如果根據類型找到多個Bean則需要選擇一個合適的Bean返回if (matchingBeans.size() > 1) {autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);if (autowiredBeanName == null) {if (isRequired(descriptor) || !indicatesArrayCollectionOrMap(type)) {// Raise exception if no clear match found for required injection pointreturn 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 {//如果只有一個Bean則直接返回這個BeanMap.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();autowiredBeanName = entry.getKey();instanceCandidate = entry.getValue();}// Step 6: validate single resultif (autowiredBeanNames != null) {autowiredBeanNames.add(autowiredBeanName);}if (instanceCandidate instanceof Class) {instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);}return resolveInstance(instanceCandidate, descriptor, type, autowiredBeanName);}finally {ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);}
}

AutowiredAnnotationBeanPostProcessor 實現邏輯(以修飾字段為例)

首先在構造函數中初始化了需要處理的注解包括 @Autowired@Inject 注解。代碼如下:

public AutowiredAnnotationBeanPostProcessor() {//添加要處理@Autowired注解this.autowiredAnnotationTypes.add(Autowired.class);this.autowiredAnnotationTypes.add(Value.class);ClassLoader classLoader = AutowiredAnnotationBeanPostProcessor.class.getClassLoader();try {//這里是為了適配不同版本@Inject注解在不同的包路徑下this.autowiredAnnotationTypes.add((Class<? extends Annotation>)ClassUtils.forName("jakarta.inject.Inject", classLoader));} catch (ClassNotFoundException ex) {// jakarta.inject API not available - simply skip.}try {//這里是為了適配不同版本@Inject注解在不同的包路徑下this.autowiredAnnotationTypes.add((Class<? extends Annotation>)ClassUtils.forName("javax.inject.Inject", classLoader));} catch (ClassNotFoundException ex) {// javax.inject API not available - simply skip.}
}

在它的 postProcessProperties() 方法中主要實現邏輯為找到 @Autowired 或者 @Inject 注解修飾的字段 -> 通過反射給字段賦值。代碼如下:

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

@Autowired 或者 @Inject 注解修飾的字段是在 findAutowiringMetadata() 方法中實現的,在該方法中又調用了 buildAutowiringMetadata() 來進行實際的查找,在這個方法中通過反射的方式遍歷字段看它是否有 @Autowired 或者 @Inject 注解修飾,如果是的話把它包裝為一個AutowiredFieldElement 對象放到列表中。最后基于列表構造一個 InjectionMetadata 對象返回。代碼如下:

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;
}private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {return InjectionMetadata.EMPTY;}final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();ReflectionUtils.doWithLocalFields(targetClass, field -> {//這里找到是否有@Autowired或者@Inject注解修飾MergedAnnotation<?> ann = findAutowiredAnnotation(field);if (ann != null) {if (Modifier.isStatic(field.getModifiers())) {return;}boolean required = determineRequiredStatus(ann);fieldElements.add(new AutowiredFieldElement(field, required));}});}
}

實際觸發賦值的操作是在 InjectionMetadatainject() 方法中實現的,在它的方法中又會循環調用 AutowiredFieldElementinject() 方法。代碼如下:

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

InjectedElementinject() 方法中通過反射的方式將找到的 Bean 賦值給字段。代碼如下:

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;if (this.cached) {//省略代碼} else {//找到對應的Beanvalue = resolveFieldValue(field, bean, beanName);}if (value != null) {ReflectionUtils.makeAccessible(field);//通過反射的方式賦值field.set(bean, value);}
}@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());Set<String> autowiredBeanNames = new LinkedHashSet<>(2);TypeConverter typeConverter = beanFactory.getTypeConverter();Object value;try {//調用beanFactory的resolveDependency()方法value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);} catch (BeansException ex) {throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);}return value;
}

然后會調用到 DefaultListableBeanFactorydoResolveDependency() 方法,和上面 @Resource 注解根據名稱找不到 Bean 需要根據類型進行匹配的調用的是一個方法,只是它會多一個分支。在這個分支里面判斷 Bean 名稱對應的 Bean 是否存在,如果存在則直接返回,如果不存在才會按照類型去匹配,這里實際上還是先按照名稱匹配的,名稱匹配不上再走的類型匹配的邏輯。代碼如下:

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);try {//省略代碼//如果是@Autowired注解或者@Inject注解會先走到下面這個分支//在這個分支里面也會先判斷對應Bean名稱的Bean是否存在,如果存在//則直接獲取返回,如果不存在才會按照類型去匹配if (descriptor.usesStandardBeanLookup()) {String dependencyName = descriptor.getDependencyName();if (dependencyName == null || !containsBean(dependencyName)) {String suggestedName = getAutowireCandidateResolver().getSuggestedName(descriptor);dependencyName = (suggestedName != null && containsBean(suggestedName) ? suggestedName : null);}if (dependencyName != null) {dependencyName = canonicalName(dependencyName);  // dependency name can be alias of target nameif (isTypeMatch(dependencyName, type) && isAutowireCandidate(dependencyName, descriptor) &&!isFallback(dependencyName) && !hasPrimaryConflict(dependencyName, type) &&!isSelfReference(beanName, dependencyName)) {if (autowiredBeanNames != null) {autowiredBeanNames.add(dependencyName);}Object dependencyBean = getBean(dependencyName);return resolveInstance(dependencyBean, descriptor, type, dependencyName);}}}//這里根據類型找到所有的Bean,然后Bean的名稱作為key,Bean作為ValueMap<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);if (matchingBeans.isEmpty()) {// Step 4c (fallback): custom Collection / Map declarations for collecting multiple beansmultipleBeans = resolveMultipleBeansFallback(descriptor, beanName, autowiredBeanNames, typeConverter);if (multipleBeans != null) {return multipleBeans;}// Raise exception if nothing found for required injection pointif (isRequired(descriptor)) {raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);}return null;}String autowiredBeanName;Object instanceCandidate;//如果根據類型找到多個Bean則需要選擇一個合適的Bean返回if (matchingBeans.size() > 1) {autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);if (autowiredBeanName == null) {if (isRequired(descriptor) || !indicatesArrayCollectionOrMap(type)) {// Raise exception if no clear match found for required injection pointreturn 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 {//如果只有一個Bean則直接返回這個BeanMap.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();autowiredBeanName = entry.getKey();instanceCandidate = entry.getValue();}// Step 6: validate single resultif (autowiredBeanNames != null) {autowiredBeanNames.add(autowiredBeanName);}if (instanceCandidate instanceof Class) {instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);}return resolveInstance(instanceCandidate, descriptor, type, autowiredBeanName);}finally {ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);}
}

當有多個類型匹配的 Bean 選擇返回一個 Bean 的原則

當根據類型找到多個 Bean 時需要根據一些規則返回一個Bean。常見的可以通過 @Qualifer 限定名稱或者通過 @Primary 來表示優先注入。在DefaultListableBeanFactordetermineAutowireCandidate() 方法中就實現了這些邏輯:

首先遍歷找到的所有符合類型的 Bean,然后看是否有 @Primary 注解修飾,如果有的話,則優先返回有該 Bean;

否則再次嘗試根據字段的名稱匹配看是否有匹配的 Bean,如果有則返回;

否則嘗試獲取 @Qualifier注解定義的名稱(對于 @Named 注解來說它本身上面也有 @Qualifer 注解修飾),然后看是否有名稱匹配的 Bean,如果有則返回;

否則遍歷 Bean 看是否有 @Priority 注解修飾,如果有則找最高優先級的 Bean 返回,值越小優先級越高;

否則看 resolvableDependencies 是否有注冊對應的實例,如果有則返回,它的使用場景一般是有用戶自己的 new 的對象可以注冊到這里面,然后在一個 Spring 管理的 Bean 中可以把它注入進來。代碼如下:

protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {Class<?> requiredType = descriptor.getDependencyType();//首先處理@Primary注解,如果某個Bean有@Primary注解修飾則優先返回它String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);if (primaryCandidate != null) {return primaryCandidate;}//否則再次根據字段的名稱進行匹配,看找到的Bean里面有沒有和字段名稱相同的Bean,有的話則優先返回String dependencyName = descriptor.getDependencyName();if (dependencyName != null) {for (String beanName : candidates.keySet()) {if (matchesBeanName(beanName, dependencyName)) {return beanName;}}}//否則嘗試獲取@Qualifier注解定義的名稱,看找打的Bean里面有沒有和該名稱相同的Bean,有的話則優先返回String suggestedName = getAutowireCandidateResolver().getSuggestedName(descriptor);if (suggestedName != null) {for (String beanName : candidates.keySet()) {if (matchesBeanName(beanName, suggestedName)) {return beanName;}}}//否則看找到的Bean是否有@Priority注解修飾,有的話取優先級最高的返回即值最小的String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);if (priorityCandidate != null) {return priorityCandidate;}//否則自定義注冊的非Spring管理生命周期的對象中是否有匹配,resolvableDependencies里面可以放//一些對象,這些對象不是由Spring創建的而是用戶自己創建放入的且需要在一個Spring的Bean中注入它for (Map.Entry<String, Object> entry : candidates.entrySet()) {String candidateName = entry.getKey();Object beanInstance = entry.getValue();if (beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) {return candidateName;}}return null;
}

@Named 注解定義中使用了 @Qualifer 注解修飾。代碼如下:

@Qualifier // 這里使用了@Qualifer注解修飾
@Documented
@Retention(RUNTIME)
public @interface Named {String value() default "";
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/902711.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/902711.shtml
英文地址,請注明出處:http://en.pswp.cn/news/902711.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Rust結構體】Rust結構體詳解:從基礎到高級應用

?? 歡迎大家來到景天科技苑?? &#x1f388;&#x1f388; 養成好習慣&#xff0c;先贊后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者簡介&#xff1a;景天科技苑 &#x1f3c6;《頭銜》&#xff1a;大廠架構師&#xff0c;華為云開發者社區專家博主&#xff0c;…

《LightLLM:開啟大語言模型推理新時代》

《LightLLM:開啟大語言模型推理新時代》 大語言模型推理的困境與挑戰 在當今人工智能飛速發展的時代,大語言模型(LLMs)無疑是最為耀眼的明星技術之一。從 OpenAI 的 GPT 系列到谷歌的 BERT,再到國內如百度文心一言、阿里通義千問等,大語言模型以其強大的語言理解和生成能…

【Python Web開發】02-Socket網絡編程02

文章目錄 1. 服務器端1.1 socket.socket()1.2 socket.bind()1.3 socket.listen()1.4 socket.accept()1.5 socket.recv()1.6 socket.send() 和 socket.sendall()1.7 socket.close() 2. 客戶端2.1 socket.socket()2.2 socket.connect()2.3 socket.send() 和 socket.sendall()2.4 …

Flutter 在全新 Platform 和 UI 線程合并后,出現了什么大坑和變化?

Flutter 在全新 Platform 和 UI 線程合并后&#xff0c;出現了什么大坑和變化&#xff1f; 在兩個月前&#xff0c;我們就聊過 3.29 上《Platform 和 UI 線程合并》的具體原因和實現方式&#xff0c;而事實上 Platform 和 UI 線程合并&#xff0c;確實為后續原生語言和 Dart 的…

藍橋杯 1. 四平方和

四平方和 原題目鏈接 題目描述 四平方和定理&#xff08;又稱拉格朗日定理&#xff09;指出&#xff1a; 每個正整數都可以表示為 至多 4 個正整數的平方和。 如果將 0 包括進去&#xff0c;則每個正整數都可以恰好表示為 4 個非負整數的平方和。 例如&#xff1a; 5 0 …

開發并發布一個屬于自己的包(npm)

一、CommonJS規范導入require 創建一個npm包涉及幾個步驟&#xff0c;包括設置你的項目結構、編寫代碼、編寫文檔、測試你的代碼&#xff0c;以及發布到npm倉庫。以下是一個基本的指南&#xff0c;幫助你從頭開始創建一個npm包。 步驟 1: 初始化npm項目 創建項目文件夾&#x…

CRTP(Curiously Recurring Template Pattern)

C 中的 CRTP&#xff08;奇異遞歸模板模式&#xff09; CRTP&#xff08;Curiously Recurring Template Pattern&#xff09;是一種利用模板繼承實現 靜態多態&#xff08;Static Polymorphism&#xff09; 的設計模式。通過基類模板以派生類作為模板參數&#xff0c;CRTP 允許…

小白工具視頻轉MPG, 功能豐富齊全,無需下載軟件,在線使用,超實用

在視頻格式轉換需求日益多樣的今天&#xff0c;小白工具網的在線視頻轉 MPG 功能https://www.xiaobaitool.net/videos/convert-to-mpg/ &#xff09;脫穎而出&#xff0c;憑借其出色特性&#xff0c;成為眾多用戶處理視頻格式轉換的優質選擇。 從格式兼容性來看&#xff0c;它支…

銀河麒麟系統離線安裝nodejs

本篇文章我們介紹如何通過nvm(node版本管理工具)來實現離線安裝nodejs 第一步&#xff1a;下載nvm https://github.com/nvm-sh/nvm/releases/tag/v0.40.1 在頁面找到【Source code(tar.gz)】下載 第二步&#xff1a;安裝nvm 將下載好的tar.gz拷貝到銀河麒麟系統文件夾下(加…

Go語言中包導入下劃線的作用解析

在Go語言的代碼中&#xff0c;有時會看到類似以下的導入語句&#xff1a; import _ "github.com/mattn/go-sqlite3"這種以下劃線_開頭的導入方式&#xff0c;顯得有些特別&#xff0c;尤其是對于新手來說&#xff0c;可能會感到困惑&#xff0c;為什么要這樣寫&…

Winddows11官網下載安裝VMware Workstation Pro17(圖文詳解)

Winddows11安裝VMware17 1、官網下載2、安裝3、總結 1、官網下載 官網地址 點擊Products&#xff0c;滑到最下面&#xff0c;選擇SEE DESKTOPP HYPERVISORS 選擇 DOWNLOAD FUSION OR WORKSTATION 自動跳轉到下面哪個服界面&#xff0c;注冊 輸入郵箱地址和圖片下面的文字…

DeepSeek智能時空數據分析(二):3秒對話式搞定“等時圈”繪制

序言&#xff1a;時空數據分析很有用&#xff0c;但是GIS/時空數據庫技術門檻太高 時空數據分析在優化業務運營中至關重要&#xff0c;然而&#xff0c;三大挑戰仍制約其發展&#xff1a;技術門檻高&#xff0c;需融合GIS理論、SQL開發與時空數據庫等多領域知識&#xff1b;空…

【Linux網絡】應用層自定義協議與序列化及Socket模擬封裝

&#x1f4e2;博客主頁&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客倉庫&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;歡迎點贊 &#x1f44d; 收藏 ?留言 &#x1f4dd; 如有錯誤敬請指正&#xff01; &…

基于大模型的結腸癌全病程預測與診療方案研究

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與創新點 二、結腸癌概述 2.1 流行病學特征 2.2 發病機制與危險因素 2.3 臨床癥狀與診斷方法 三、大模型技術原理與應用現狀 3.1 大模型的基本原理 3.2 在醫療領域的應用情況 3.3 在結腸癌預測中的潛力分析 四、術前…

【UML建模】starUML工具

一.概述 StarUML是一款UML工具&#xff0c;允許用戶創建和管理UML&#xff08;統一建模語言&#xff09;模型&#xff0c;廣泛應用于軟件工程領域。它的主要功能包括創建各種UML圖&#xff1a;如用例圖、類圖、序列圖等&#xff0c;支持代碼生成與反向工程&#xff0c;以及提供…

模板元編程(Template Metaprogramming, TMP)

C 模板元編程&#xff08;Template Metaprogramming, TMP&#xff09; 模板元編程是一種利用 C 模板系統在 編譯期間 完成計算、類型操作和代碼生成的編程范式。其核心優勢在于通過 零運行時開銷 實現高效、類型安全的代碼。以下是模板元編程的詳細分步解析。 1. 編譯時計算 …

Android Build Variants(構建變體)詳解

Android Build Variants&#xff08;構建變體&#xff09;是 Android 開發中用于生成不同版本應用程序的一種機制。它允許開發者根據不同的需求&#xff0c;如不同的應用市場、不同的功能模塊、不同的環境配置等&#xff0c;從同一個代碼庫中生成多個不同的 APK。 組成部分 B…

26考研|數學分析:數項級數

數項級數這一章的開始&#xff0c;開啟了新的關于“級數”這一新的概念體系的學習進程&#xff0c;此部分共包含四章的內容&#xff0c;分別為數項級數、函數項級數、冪級數以及傅里葉級數。這一章中&#xff0c;首先要掌握級數的相關概念與定義&#xff0c;重難點在于掌握判斷…

擁抱健康生活,解鎖養生之道

在生活節奏日益加快的當下&#xff0c;健康養生已成為人們關注的焦點。科學的養生方法&#xff0c;能幫助我們增強體質、預防疾病&#xff0c;以更飽滿的精神狀態擁抱生活。 合理飲食是養生的基石。《黃帝內經》中提到 “五谷為養&#xff0c;五果為助&#xff0c;五畜為益&…

房地產安裝工程師簡歷模板

模板信息 簡歷范文名稱&#xff1a;房地產安裝工程師簡歷模板&#xff0c;所屬行業&#xff1a;其他 | 職位&#xff0c;模板編號&#xff1a;XUCP9X 專業的個人簡歷模板&#xff0c;邏輯清晰&#xff0c;排版簡潔美觀&#xff0c;讓你的個人簡歷顯得更專業&#xff0c;找到好…