news 2026/10/3 8:39:06

spring-reading 源码精读:JSR-330 @Qualifier 限定符注解的注册与依赖解析全链路剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
spring-reading 源码精读:JSR-330 @Qualifier 限定符注解的注册与依赖解析全链路剖析
  • 示例工程
  • 文档

【免费下载链接】spring-reading

涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。

项目地址:https://gitcode.com/GitHub_Trending/sp/spring-reading
点击查看免费下载

本文基于 spring-reading 仓库中的 spring-jsr330-qualifier 模块,以源码级视角深入讲解 JSR-330 规范中@Qualifier注解的设计定位、使用方式与底层实现。文章完整继承原文档的最佳实践示例(自定义限定符注解 +@Inject组合注入),并结合 Spring 源码还原QualifierAnnotationAutowireCandidateResolver从注册到解析的完整调用链,帮助读者既能在实战中利用@Qualifier解决同类型 Bean 的注入歧义,又能读懂 Spring 容器内部"谁有资格被注入"的决策机制。

@Qualifier是 Java 标准注解体系(JSR 330: Dependency Injection for Java)中的一员,专门用于解决依赖注入中的歧义性:当容器中存在多个同类型的 Bean 或实例可供选择时,它指导容器明确选择哪一个进行注入。在 spring-reading 仓库中,spring-jsr330-qualifier 模块以"消息服务(Email / SMS)双实现"为场景,演示了如何基于@Qualifier元注解自定义@Email、@SMS限定符,并与@Inject协同完成精准注入。读完本文,你将掌握:@Qualifier的注解源码与其两种形态(Spring 注解 / JSR-330 元注解);自定义限定符注解的完整落地代码;以及从AnnotationConfigApplicationContext初始化到QualifierAnnotationAutowireCandidateResolver#checkQualifiers的源码级解析链路。

一、@Qualifier 注解描述:为什么需要它

依赖注入的核心思想是把"选谁"的决定权交给容器。但当容器中存在多个同类型候选时,注入本身就会产生歧义。例如本文示例中的MessageService同时存在EmailServiceImpl与SMSServiceImpl两个实现,若不加以限定,容器无法判断@Inject注入点该匹配哪一个。

@Qualifier正是为此而生——它作为限定符(Qualifier),在注入点标注"我要哪一个",在 Bean 定义上声明"我是哪一个",从而消除歧义。在 JSR-330 规范中,@Qualifier被设计为元注解(meta-annotation),即它本身不直接使用,而是用于创建新的自定义注解,由这些自定义注解充当具体的限定符。

二、注解源码剖析:一个空注解如何承载语义

JSR-330 中javax.inject.Qualifier的源码极其精简,本身不携带任何属性:

@Target({ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Qualifier { }

从源码结构可以提炼出三个关键设计点:

  • @Target(ElementType.ANNOTATION_TYPE):限定@Qualifier只能修饰注解类型,即它只能作为元注解存在——这与 JSR-330 中"通过自定义限定符注解解决歧义"的目的完全一致;
  • @Retention(RetentionPolicy.RUNTIME):保留到运行时,使容器可以在运行时通过反射读取限定符信息,这是依赖注入能够实现的前提;
  • 空注解体:@Qualifier本身不携带 value 等属性,具体的限定语义由基于它的自定义注解(如@Email、@SMS)来承载。

需要区分的是:Spring 框架中的org.springframework.beans.factory.annotation.Qualifier与 JSR-330 的javax.inject.Qualifier形态略有不同。Spring 版本带有value属性,支持@Qualifier("beanName")这种按名称限定;而javax.inject.Qualifier是纯粹的元注解,更强调"自定义限定符"这一扩展方式。本模块使用的是后者(javax.inject包),依赖声明见 spring-jsr/pom.xml(javax.inject:javax.inject:1)。

三、@Qualifier 的主要功能

  1. 解决歧义性:当容器中存在多个同一类型的 Bean 时,@Qualifier用于指定哪一个具体的 Bean 应被注入。
  2. 与@Autowired/@Inject配合使用:
    • 在 Spring 中,org.springframework.beans.factory.annotation.Qualifier通常与org.springframework.beans.factory.annotation.Autowired配合,从 Spring 容器解析 Bean 时解决歧义;
    • 在 JSR-330 规范(如 CDI)中,javax.inject.Qualifier是元注解,用于定义新的限定符注解,再与javax.inject.Inject配合解决歧义。
  3. 自定义限定符注解:尤其在 JSR-330 规范中,@Qualifier用于创建其他自定义注解,新注解随后作为限定符使用。
  4. 提高代码语义清晰度:通过@Qualifier或基于它的自定义注解,代码更具描述性,读者一眼就能看出注入点期望的是哪个具体 Bean。

四、最佳实践:自定义限定符 + @Inject 组合注入

本节完整复现 spring-jsr330-qualifier 模块的实战代码,所有源码文件均可直接在本仓库中查阅、运行。

4.1 启动类入口

启动类使用AnnotationConfigApplicationContext(基于 Java 注解配置 Spring 容器的方式),构造参数传入配置类MyConfiguration,随后从上下文获取MessageController并调用showMessage方法。完整源码见 QualifierApplication.java。

public class QualifierApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MessageController messageController = context.getBean(MessageController.class); messageController.showMessage(); } }

4.2 配置类:组件扫描

MyConfiguration上标注@ComponentScan("com.xcs.spring"),告诉 Spring 在指定包及其子包中搜索带@Component、@Service、@Repository、@Controller等注解的类并自动注册为 Bean,从而省去显式编写每个 Bean 定义。完整源码见 MyConfiguration.java。

@Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { }

4.3 自定义限定符注解:@Email 与 @SMS

定义两个自定义注解@Email和@SMS,二者均被@Qualifier(javax.inject.Qualifier)标注,意味着它们都可被用作限定符注解。完整源码见 Email.java 与 SMS.java。

@Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Email { } @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface SMS { }

注意两个细节:其一,此处引入的是javax.inject.Qualifier元注解;其二,自定义注解必须声明@Retention(RetentionPolicy.RUNTIME),否则运行时无法被容器读取,限定符将失效。

4.4 服务接口与两个实现

EmailServiceImpl与SMSServiceImpl都实现MessageService接口,但分别标注@Email与@SMS限定符(同时标注@Named使它们成为可被容器识别的命名 Bean),保证注入时可被明确区分。完整源码见 MessageService.java、EmailServiceImpl.java、SMSServiceImpl.java。

public interface MessageService { String getMessage(); } @Email @Named public class EmailServiceImpl implements MessageService { @Override public String getMessage() { return "Email message"; } } @SMS @Named public class SMSServiceImpl implements MessageService { @Override public String getMessage() { return "SMS message"; } }

4.5 注入点:@Inject + 限定符组合

MessageController中注入两个MessageService实现:字段emailService使用@Inject+@Email,字段smsService使用@Inject+@SMS,通过自定义限定符确保正确的实现被注入到对应字段。完整源码见 MessageController.java。

@Controller public class MessageController { @Inject @Email private MessageService emailService; @Inject @SMS private MessageService smsService; public void showMessage() { System.out.println("EmailService: " + emailService.getMessage()); System.out.println("SMSService: " + smsService.getMessage()); } }

4.6 运行结果

运行QualifierApplication后输出如下,说明限定符起到了关键作用:

EmailService: Email message SMSService: SMS message

@Email限定符确保了EmailServiceImpl注入到emailService字段,@SMS限定符确保了SMSServiceImpl注入到smsService字段,运行结果完全符合预期。

五、时序图:注册与解析两条链路

@Qualifier的生效涉及两条链路:注册链路(容器初始化时将javax.inject.Qualifier登记为可识别的限定符类型)与解析链路(依赖注入时对注入点注解与候选 Bean 进行限定符匹配)。

5.1 @Qualifier 注册时序图

5.2 @Qualifier 解析时序图

六、源码分析:@Qualifier 的底层实现

6.1 @Qualifier 注册链路

从启动类QualifierApplication出发,AnnotationConfigApplicationContext的构造函数执行了三个步骤,本次分析重点是this():

public AnnotationConfigApplicationContext(Class<?>... componentClasses) { this(); register(componentClasses); refresh(); }

在AnnotationConfigApplicationContext()无参构造中,初始化了两个核心组件:一个用于读取注解定义 Bean 的AnnotatedBeanDefinitionReader(本次重点),一个用于扫描类路径并自动检测 Bean 组件的ClassPathBeanDefinitionScanner:

public AnnotationConfigApplicationContext() { StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create"); this.reader = new AnnotatedBeanDefinitionReader(this); createAnnotatedBeanDefReader.end(); this.scanner = new ClassPathBeanDefinitionScanner(this); }

AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(registry)继续转发到带Environment的构造器:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); }

带Environment的构造器内部调用了关键方法registerAnnotationConfigProcessors,它会向容器注册一系列后置处理器,这些后置处理器对处理@Inject、@Qualifier等注解至关重要:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }

AnnotationConfigUtils#registerAnnotationConfigProcessors(registry)再次转发到重载方法:

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, null); }

在registerAnnotationConfigProcessors(registry, source)中:Spring 自动装配机制在多个候选 Bean 可供注入时,需要一种方式决定哪个是最佳候选,AutowireCandidateResolver就是负责该决策的组件。方法内将默认解析器替换为ContextAnnotationAutowireCandidateResolver(其父类正是QualifierAnnotationAutowireCandidateResolver):

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { // ... [代码部分省略以简化] if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } // ... [代码部分省略以简化] }

最后,QualifierAnnotationAutowireCandidateResolver的构造器完成 JSR-330 限定符的"登记":尝试加载javax.inject.Qualifier并加入qualifierTypes集合。如果类路径上不存在 JSR-330 API,则静默跳过(catch 后不处理),这也是该模块在 spring-jsr/pom.xml 中声明javax.inject:javax.inject:1依赖的原因:

public QualifierAnnotationAutowireCandidateResolver() { // ... [代码部分省略以简化] try { this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader())); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }

6.2 @Qualifier 解析链路

前置条件

在 Spring 中,AutowiredAnnotationBeanPostProcessor负责处理多种依赖注入注解,包括 JSR-330 的@Inject。要完整理解@Inject与@Qualifier的协同工作原理,需要先掌握@Inject的处理机制——可参考本仓库中的 spring-jsr330-inject 模块,它从 JSR-330 规范引入到 Spring 中的用法,以及@Inject与@Autowired的差异提供了全面视角。

解析入口

DefaultListableBeanFactory#resolveDependency首先尝试获取延迟解析代理,无法获取时继续调用doResolveDependency完成实际解析:

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; }

doResolveDependency的核心是步骤 1:根据依赖描述符查找匹配的 Bean:

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // 步骤1. 根据依赖描述符查找匹配的bean Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); // ... [代码部分省略以简化] } // ... [代码部分省略以简化] }

findAutowireCandidates先按类型获取所有可能的 Bean 名,再逐个判断候选 Bean 是否为合适的自动装配候选,将合适的加入结果集:

protected Map<String, Object> findAutowireCandidates( @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { // 根据所需的类型,包括所有父工厂中的bean,获取所有可能的bean名 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); // ... [代码部分省略以简化] // 遍历所有候选bean名 for (String candidate : candidateNames) { // 如果候选bean不是正在查找的bean本身并且它是一个合适的自动注入候选 if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { // 添加这个候选bean到结果中 addCandidateEntry(result, candidate, descriptor, requiredType); } } // ... [代码部分省略以简化] // 返回找到的所有候选bean return result; }

isAutowireCandidate(beanName, descriptor)获取注册链路中设置的解析器(即QualifierAnnotationAutowireCandidateResolver),并转发到带解析器的重载版本——当存在多个候选时,解析器会考虑@Qualifier及相关注解:

@Override public boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor) throws NoSuchBeanDefinitionException { return isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver()); }

该重载版本获取合并后的 Bean 定义(getMergedLocalBeanDefinition),继续转发:

protected boolean isAutowireCandidate( String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver) throws NoSuchBeanDefinitionException { String bdName = BeanFactoryUtils.transformedBeanName(beanName); if (containsBeanDefinition(bdName)) { return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(bdName), descriptor, resolver); } // ... [代码部分省略以简化] }

最内层重载构建BeanDefinitionHolder(同时持有BeanDefinition、Bean 名称与别名),然后交由解析器的isAutowireCandidate(holder, descriptor)裁决:

protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor, AutowireCandidateResolver resolver) { // ... [代码部分省略以简化] BeanDefinitionHolder holder = (beanName.equals(bdName) ? this.mergedBeanDefinitionHolders.computeIfAbsent(beanName, key -> new BeanDefinitionHolder(mbd, beanName, getAliases(bdName))) : new BeanDefinitionHolder(mbd, beanName, getAliases(bdName))); return resolver.isAutowireCandidate(holder, descriptor); }
核心裁决:QualifierAnnotationAutowireCandidateResolver

QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate增强了对@Qualifier的支持:若 Bean 或依赖被@Qualifier标注并指定了 Bean 名称,则仅名称匹配的 Bean 被视为候选;若未指定名称但存在其他属性或元注解,则仅匹配这些属性/元注解的 Bean 被视为候选:

@Override public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) { // 首先通过父类的方法进行基本的匹配检查。 boolean match = super.isAutowireCandidate(bdHolder, descriptor); // 如果基本检查通过,则进一步检查 bdHolder 和 descriptor 中的注解是否匹配。 if (match) { // 检查候选bean的资格符与依赖描述符的注解是否匹配。 match = checkQualifiers(bdHolder, descriptor.getAnnotations()); if (match) { // 获取与依赖相关的方法参数(如果有的话,例如当依赖是一个setter方法的参数时)。 MethodParameter methodParam = descriptor.getMethodParameter(); // 如果有关联的方法参数,则进行进一步的检查。 if (methodParam != null) { Method method = methodParam.getMethod(); // 如果方法是void返回类型(如setter方法),则检查它的注解与候选bean的资格符是否匹配。 if (method == null || void.class == method.getReturnType()) { match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations()); } } } } // 返回最终的匹配结果。 return match; }

checkQualifiers负责确保待注入的 Bean 与所有相关@Qualifier注解及元注解匹配,只要有一个不匹配,该 Bean 就不是当前注入点的合适候选:

protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) { // 如果注解数组为空,直接返回 true if (ObjectUtils.isEmpty(annotationsToSearch)) { return true; } SimpleTypeConverter typeConverter = new SimpleTypeConverter(); for (Annotation annotation : annotationsToSearch) { Class<? extends Annotation> type = annotation.annotationType(); boolean checkMeta = true; boolean fallbackToMeta = false; // 检查当前注解是否是 @Qualifier 或自定义的资格符注解 if (isQualifier(type)) { // 如果是,并且与bean定义匹配 if (!checkQualifier(bdHolder, annotation, typeConverter)) { // 不匹配则可能需要检查元注解 fallbackToMeta = true; } else { // 匹配则不需要进一步检查元注解 checkMeta = false; } } // 如果不是资格符注解或与bean定义不匹配,但有元注解是资格符注解 if (checkMeta) { boolean foundMeta = false; for (Annotation metaAnn : type.getAnnotations()) { Class<? extends Annotation> metaType = metaAnn.annotationType(); // 检查元注解是否是资格符注解 if (isQualifier(metaType)) { foundMeta = true; // 只有当 @Qualifier 注解有值或其他条件满足时,才会接受元注解的匹配 if ((fallbackToMeta && ObjectUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || !checkQualifier(bdHolder, metaAnn, typeConverter)) { // 元注解不匹配 return false; } } } if (fallbackToMeta && !foundMeta) { return false; // 需要元注解,但没有找到 } } } // 所有注解都匹配 return true; }

最后,isQualifier判定给定注解是否为"资格符"注解(限定符注解),通过检查注解是否存在于已知的qualifierTypes集合中、或是否带有这些资格符元注解来完成:

protected boolean isQualifier(Class<? extends Annotation> annotationType) { // 遍历已知的资格符注解类型 for (Class<? extends Annotation> qualifierType : this.qualifierTypes) { // 如果给定的注解是已知的资格符注解,或者它带有这样的元注解 if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) { return true; // 是资格符注解 } } return false; // 不是资格符注解 }

结合本文示例回看:EmailServiceImpl上标注的@Email带有元注解@Qualifier,因此在isQualifier检查中会被识别为限定符注解;注入点emailService字段上的@Email与候选 Bean 的限定符匹配成功,于是EmailServiceImpl成为唯一合适候选并被注入。

七、注意事项

  1. 确保正确的依赖注入:使用@Qualifier是为了解决容器中多个同类型 Bean 的歧义问题。没有@Qualifier,Spring 无法决定注入哪一个;但使用时必须确保给定的限定符名称确实存在,否则 Spring 会抛出异常。
  2. 与其他注解组合:@Qualifier通常与@Autowired或@Inject一起使用,并且要放在正确的位置(字段、setter 方法或构造函数)。
  3. 自定义限定符注解:可以创建自己的注解并用@Qualifier作为元注解,从而得到具有特定命名或其他语义的限定符注解,让代码更具可读性。
  4. 使用字符串名称:Spring 版@Qualifier默认使用字符串值指定 Bean 名称;一旦重命名 Bean,所有引用该名称的@Qualifier注解也需要同步修改。
  5. 与 Java 配置一起使用:使用 Java 配置创建 Bean 时,可以用@Bean方法名作为限定符名称,使 Java 配置与@Qualifier的使用保持一致。
  6. 与@Primary的关系:若同时使用@Primary与@Qualifier,@Qualifier的优先级更高——即使某 Bean 被标记为@Primary,注入点使用了@Qualifier时仍会选用@Qualifier指定的 Bean。
  7. 名称与类型的匹配:尽管@Qualifier主要用于按名称匹配,Spring 仍会校验匹配 Bean 的类型;若名称匹配但类型不匹配,依然会抛出异常。
  8. 与 JSR-330 的兼容性:Spring 的@Qualifier与 JSR-330 的javax.inject.Qualifier兼容;但按 JSR-330 标准使用时,需确保依赖注入提供程序(如 Spring)正确支持它。

八、总结

8.1 最佳实践总结

  1. 明确的配置:使用AnnotationConfigApplicationContext作为容器并通过构造参数指定配置类,初始化过程清晰明了。
  2. 利用组件扫描:通过@ComponentScan自动扫描包及其子包中的组件,减少手动注册 Bean 的工作,代码更简洁高效。
  3. 自定义限定符注解:为解决同类型多 Bean 问题,定义@Email与@SMS两个自定义限定符注解,使代码更有可读性、注入意图更明确。
  4. 准确的服务注入:在MessageController中,自定义的@Email、@SMS注解与@Inject组合,确保正确的MessageService实现注入到相应字段。
  5. 清晰的输出:showMessage方法的输出直观印证了@Qualifier的作用,不同服务实现被正确注入。
  6. 充分利用 Java 配置:通过@Configuration与@ComponentScan结合 Java 配置,容器初始化与 Bean 注册都更直观、简洁。
  7. 注解的扩展性:以@Qualifier为元注解自定义新注解的方式,体现了 Spring 的灵活性与注解的扩展能力。
  8. 保持代码整洁和模块化:每个类、接口职责明确,代码组织清晰、模块化,易于维护,可读性与可复用性俱佳。

8.2 源码分析总结

  1. 应用启动及上下文初始化:创建AnnotationConfigApplicationContext时接收配置类参数以初始化上下文;启动后从容器获取并使用相应 Bean。
  2. 构造过程:构造函数执行多个关键步骤,核心是this(),它初始化了AnnotatedBeanDefinitionReader(读取注解 Bean)与ClassPathBeanDefinitionScanner(扫描类路径检测组件)。
  3. AnnotatedBeanDefinitionReader 的初始化:其构造函数调用registerAnnotationConfigProcessors,向容器注册一系列后置处理器,这对处理@Inject、@Qualifier等注解至关重要。
  4. @Qualifier 的注册:创建QualifierAnnotationAutowireCandidateResolver时尝试加载 JSR-330 的@Qualifier并加入qualifierTypes集合,使后续依赖注入能正确处理该注解。
  5. 依赖解析过程:Spring 解析依赖时进入DefaultListableBeanFactory#resolveDependency,先尝试延迟解析代理,失败则经doResolveDependency完成解析。
  6. 自动装配候选检查:findAutowireCandidates找出所有可能的 Bean 名,再逐个经isAutowireCandidate判断是否为合适的自动装配候选。
  7. AutowireCandidateResolver 的决策:存在多个候选时,AutowireCandidateResolver(此处为QualifierAnnotationAutowireCandidateResolver)综合@Qualifier及相关注解做出最终选择。
  8. 处理 @Qualifier 注解:QualifierAnnotationAutowireCandidateResolver的核心职责是确保待注入 Bean 与所有相关@Qualifier注解及元注解匹配,任一不匹配即排除该候选。
  9. 资格符检查:isQualifier通过检查注解是否存在于qualifierTypes集合、或是否带有这些资格符元注解,判定某注解是否为限定符注解。

至此,从 JSR-330 规范定位、自定义限定符实践到 Spring 源码注册/解析全链路,@Qualifier的完整工作机制已清晰呈现。读者可直接在 spring-jsr330-qualifier 模块 中运行示例验证,并参照 spring-jsr330-inject 模块 补全@Inject一侧的知识拼图。

  • 示例工程
  • 文档

【免费下载链接】spring-reading

涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。

项目地址:https://gitcode.com/GitHub_Trending/sp/spring-reading
点击查看免费下载
上一篇:OpenMontage 前端组合模式实战:用 Provider 将状态管理与 UI 解耦
下一篇:Implementation Plan

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 8:39:05

AIMATEX-NAS:个人免费云盘

它是什么个人数仓&#xff1a;高速云盘 冷备云盘 Windows 本地文件夹共享。 云端能存&#xff0c;家里硬盘也能远程当网盘用。合理定价&#xff0c;随用随充。能干什么高速云盘 日常文件上传下载&#xff0c;尽量打满你的带宽。冷备云盘 大文件归档备份&#xff0c;适合不常访…

作者头像 李华
网站建设 2026/10/3 8:36:51

FaceFusion force-download 完整指南:3 步搞定模型下载与哈希校验

FaceFusion force-download 完整指南&#xff1a;3 步搞定模型下载与哈希校验 【免费下载链接】facefusion Industry leading face manipulation platform 项目地址: https://gitcode.com/GitHub_Trending/fa/facefusion 把 FaceFusion 搬上内网机器&#xff0c;模型下载…

作者头像 李华