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.mzph.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;找到好…