- 示例工程
- 文档
【免费下载链接】spring-reading
涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。
本文基于 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 的主要功能
- 解决歧义性:当容器中存在多个同一类型的 Bean 时,
@Qualifier用于指定哪一个具体的 Bean 应被注入。 - 与
@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配合解决歧义。
- 在 Spring 中,
- 自定义限定符注解:尤其在 JSR-330 规范中,
@Qualifier用于创建其他自定义注解,新注解随后作为限定符使用。 - 提高代码语义清晰度:通过
@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成为唯一合适候选并被注入。
七、注意事项
- 确保正确的依赖注入:使用
@Qualifier是为了解决容器中多个同类型 Bean 的歧义问题。没有@Qualifier,Spring 无法决定注入哪一个;但使用时必须确保给定的限定符名称确实存在,否则 Spring 会抛出异常。 - 与其他注解组合:
@Qualifier通常与@Autowired或@Inject一起使用,并且要放在正确的位置(字段、setter 方法或构造函数)。 - 自定义限定符注解:可以创建自己的注解并用
@Qualifier作为元注解,从而得到具有特定命名或其他语义的限定符注解,让代码更具可读性。 - 使用字符串名称:Spring 版
@Qualifier默认使用字符串值指定 Bean 名称;一旦重命名 Bean,所有引用该名称的@Qualifier注解也需要同步修改。 - 与 Java 配置一起使用:使用 Java 配置创建 Bean 时,可以用
@Bean方法名作为限定符名称,使 Java 配置与@Qualifier的使用保持一致。 - 与
@Primary的关系:若同时使用@Primary与@Qualifier,@Qualifier的优先级更高——即使某 Bean 被标记为@Primary,注入点使用了@Qualifier时仍会选用@Qualifier指定的 Bean。 - 名称与类型的匹配:尽管
@Qualifier主要用于按名称匹配,Spring 仍会校验匹配 Bean 的类型;若名称匹配但类型不匹配,依然会抛出异常。 - 与 JSR-330 的兼容性:Spring 的
@Qualifier与 JSR-330 的javax.inject.Qualifier兼容;但按 JSR-330 标准使用时,需确保依赖注入提供程序(如 Spring)正确支持它。
八、总结
8.1 最佳实践总结
- 明确的配置:使用
AnnotationConfigApplicationContext作为容器并通过构造参数指定配置类,初始化过程清晰明了。 - 利用组件扫描:通过
@ComponentScan自动扫描包及其子包中的组件,减少手动注册 Bean 的工作,代码更简洁高效。 - 自定义限定符注解:为解决同类型多 Bean 问题,定义
@Email与@SMS两个自定义限定符注解,使代码更有可读性、注入意图更明确。 - 准确的服务注入:在
MessageController中,自定义的@Email、@SMS注解与@Inject组合,确保正确的MessageService实现注入到相应字段。 - 清晰的输出:
showMessage方法的输出直观印证了@Qualifier的作用,不同服务实现被正确注入。 - 充分利用 Java 配置:通过
@Configuration与@ComponentScan结合 Java 配置,容器初始化与 Bean 注册都更直观、简洁。 - 注解的扩展性:以
@Qualifier为元注解自定义新注解的方式,体现了 Spring 的灵活性与注解的扩展能力。 - 保持代码整洁和模块化:每个类、接口职责明确,代码组织清晰、模块化,易于维护,可读性与可复用性俱佳。
8.2 源码分析总结
- 应用启动及上下文初始化:创建
AnnotationConfigApplicationContext时接收配置类参数以初始化上下文;启动后从容器获取并使用相应 Bean。 - 构造过程:构造函数执行多个关键步骤,核心是
this(),它初始化了AnnotatedBeanDefinitionReader(读取注解 Bean)与ClassPathBeanDefinitionScanner(扫描类路径检测组件)。 - AnnotatedBeanDefinitionReader 的初始化:其构造函数调用
registerAnnotationConfigProcessors,向容器注册一系列后置处理器,这对处理@Inject、@Qualifier等注解至关重要。 - @Qualifier 的注册:创建
QualifierAnnotationAutowireCandidateResolver时尝试加载 JSR-330 的@Qualifier并加入qualifierTypes集合,使后续依赖注入能正确处理该注解。 - 依赖解析过程:Spring 解析依赖时进入
DefaultListableBeanFactory#resolveDependency,先尝试延迟解析代理,失败则经doResolveDependency完成解析。 - 自动装配候选检查:
findAutowireCandidates找出所有可能的 Bean 名,再逐个经isAutowireCandidate判断是否为合适的自动装配候选。 - AutowireCandidateResolver 的决策:存在多个候选时,
AutowireCandidateResolver(此处为QualifierAnnotationAutowireCandidateResolver)综合@Qualifier及相关注解做出最终选择。 - 处理 @Qualifier 注解:
QualifierAnnotationAutowireCandidateResolver的核心职责是确保待注入 Bean 与所有相关@Qualifier注解及元注解匹配,任一不匹配即排除该候选。 - 资格符检查:
isQualifier通过检查注解是否存在于qualifierTypes集合、或是否带有这些资格符元注解,判定某注解是否为限定符注解。
至此,从 JSR-330 规范定位、自定义限定符实践到 Spring 源码注册/解析全链路,@Qualifier的完整工作机制已清晰呈现。读者可直接在 spring-jsr330-qualifier 模块 中运行示例验证,并参照 spring-jsr330-inject 模块 补全@Inject一侧的知识拼图。
- 示例工程
- 文档
【免费下载链接】spring-reading
涵盖了 Spring 框架的核心概念和关键功能,包括控制反转(IOC)容器的使用,面向切面编程(AOP)的原理与实践,事务管理的方式与实现,Spring MVC 的流程与控制器工作机制,以及 Spring 中数据访问、安全、Boot 自动配置等方面的深入研究。此外,它还包含了 Spring 事件机制的应用、高级主题如缓存抽象和响应式编程,以及对 Spring 源码的编程风格与设计模式的深入探讨。
相关推荐
解决依赖注入歧义:mini-spring Qualifier注解深度解析
解决依赖注入歧义:mini spring Qualifier注解深度解析 在依赖注入(Dependency Injection, DI)的世界里,当容器中存在多
后端教程spring-reading 源码实战:AnnotationConfigApplicationContext 中 Bean 定义注册的两种路径(register 与 scan 全解析)
spring reading 源码实战:AnnotationConfigApplicationContext 中 Bean 定义注册的两种路径(register
示例工程文档Egg 依赖注入完全指南:@Inject 装饰器用法、名称解析与 Qualifier 限定符实战
Egg 依赖注入完全指南:@Inject 装饰器用法、名称解析与 Qualifier 限定符实战 本篇技术指南以 packages/skills/egg cor
后端Web框架
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考