## @Qualifier - [@Qualifier](#qualifier) - [一、基本信息](#一基本信息) - [二、注解描述](#二注解描述) - [三、注解源码](#三注解源码) - [四、主要功能](#四主要功能) - [五、最佳实践](#五最佳实践) - [六、时序图](#六时序图) - [@Qualifier注册](#qualifier注册) - [@Qualifier解析](#qualifier解析) - [七、源码分析](#七源码分析) - [@Qualifier注册](#qualifier注册-1) - [@Qualifier解析](#qualifier解析-1) - [前置条件](#前置条件) - [解析入口](#解析入口) - [八、注意事项](#八注意事项) - [九、总结](#九总结) - [最佳实践总结](#最佳实践总结) - [源码分析总结](#源码分析总结) ### 一、基本信息 ✒️ **作者** - Lex 📝 **博客** - [我的CSDN]() 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Qualifier源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-jsr/spring-jsr330-qualifier) ### 二、注解描述 `@Qualifier` 注解是 Java 的标准注解,来源于 JSR 330: Dependency Injection for Java。主要用于解决依赖注入中的歧义性,当有多个同类型的 bean 或实例可供选择时,指导容器明确选择哪一个进行注入。 ### 三、注解源码 `@Qualifier` 表示它是一个基础注解,主要用于创建其他的自定义注解,这些新的注解通常用于限定和解决依赖注入时的歧义性。这与 Spring 和 JSR 330 中 `@Qualifier` 的定义和目的是一致的。 ```java @Target({ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface 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。 ### 五、最佳实践 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showMessage`方法。 ```java public class QualifierApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MessageController messageController = context.getBean(MessageController.class); messageController.showMessage(); } } ``` 在`MyConfiguration`类中,使用了`@ComponentScan("com.xcs.spring")`注解告诉 Spring 在指定的包(在这里是 "`com.xcs.spring`")及其子包中搜索带有 `@Component`、`@Service`、`@Repository` 和 `@Controller` 等注解的类,并将它们自动注册为 beans。这样,spring就不必为每个组件明确写一个 bean 定义。Spring 会自动识别并注册它们。 ```java @Configuration @ComponentScan("com.xcs.spring") public class MyConfiguration { } ``` 我们定义了两个自定义注解,`@Email` 和 `@SMS`,它们都被标记为 `@Qualifier`。这意味着它们都可以被用作限定符注解。 ```java @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface Email { } @Qualifier @Retention(RetentionPolicy.RUNTIME) public @interface SMS { } ``` `EmailServiceImpl` & `SMSServiceImpl`这两个类都实现了 `MessageService` 接口,但分别带有 `@Email` 和 `@SMS` 限定符注解,意味着它们在被注入时可以被明确区分。 ```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"; } } ``` `MessageController` 注入了两种 `MessageService` 的实现:一种用于Email,另一种用于SMS。通过使用自定义的限定符注解 `@Email` 和 `@SMS`,确保了正确的服务实现被注入到对应的字段中。`showMessage` 方法用于显示这两个服务实现返回的消息。 ```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()); } } ``` 运行结果发现,`@Email` 限定符确保了 `EmailServiceImpl` 被注入到 `emailService` 字段。`@SMS` 限定符确保了 `SMSServiceImpl` 被注入到 `smsService` 字段。`@Qualifier` 注解在这里起到了关键的作用,确保了正确的服务实现被注入,从而使得运行结果符合预期。 ```java EmailService: Email message SMSService: SMS message ``` ### 六、时序图 #### @Qualifier注册 ~~~mermaid sequenceDiagram Title: @Qualifier注册时序图 QualifierApplication->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext(componentClasses) Note over AnnotationConfigApplicationContext: 上下文初始化开始,传入了一些组件类 AnnotationConfigApplicationContext->>AnnotationConfigApplicationContext:AnnotationConfigApplicationContext() Note over AnnotationConfigApplicationContext: 上下文构造器调用 AnnotationConfigApplicationContext->>AnnotatedBeanDefinitionReader:AnnotatedBeanDefinitionReader(registry) Note over AnnotatedBeanDefinitionReader: 为给定的注册中心创建一个Bean定义读取器 AnnotatedBeanDefinitionReader->>AnnotatedBeanDefinitionReader:AnnotatedBeanDefinitionReader(registry,environment) Note over AnnotatedBeanDefinitionReader: 读取器构造器使用特定的环境设置 AnnotatedBeanDefinitionReader->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry) Note over AnnotationConfigUtils: 注册注解配置处理器 AnnotationConfigUtils->>AnnotationConfigUtils:registerAnnotationConfigProcessors(registry,source) Note right of AnnotationConfigUtils: 注册注解配置处理器 AnnotationConfigUtils->>QualifierAnnotationAutowireCandidateResolver:QualifierAnnotationAutowireCandidateResolver() Note over QualifierAnnotationAutowireCandidateResolver: 创建一个新的限定符自动装配候选解析器 QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:this.qualifierTypes.add("javax.inject.Qualifier"); Note right of QualifierAnnotationAutowireCandidateResolver: 添加javax.inject.Qualifier到限定符类型列表 QualifierAnnotationAutowireCandidateResolver->>AnnotationConfigUtils:返回Resolver Note right of AnnotationConfigUtils: 解析器创建完毕,现在返回给调用者 AnnotationConfigUtils->>DefaultListableBeanFactory:setAutowireCandidateResolver(autowireCandidateResolver) Note over DefaultListableBeanFactory: 在Bean工厂中设置自动装配候选解析器 ~~~ #### @Qualifier解析 ~~~mermaid sequenceDiagram Title: @Qualifier解析时序图 InjectionMetadata->>AutowiredFieldElement:inject(bean,beanName,pvs) Note over AutowiredFieldElement: 开始注入字段 AutowiredFieldElement->>AutowiredFieldElement:resolveFieldValue(field, bean, beanName) Note over AutowiredFieldElement: 尝试解析字段的值 AutowiredFieldElement->>DefaultListableBeanFactory:resolveDependency(desc,beanName,autowiredBeanNames,typeConverter) Note over DefaultListableBeanFactory: 解决字段的依赖关系 DefaultListableBeanFactory->>DefaultListableBeanFactory:doResolveDependency(descriptor,requestingBeanName,autowiredBeanNames,typeConverter) Note over DefaultListableBeanFactory: 执行依赖解析 DefaultListableBeanFactory->>DefaultListableBeanFactory:findAutowireCandidates(beanName,type,descriptor) Note over DefaultListableBeanFactory: 查找可能的自动装配候选者 DefaultListableBeanFactory->>DefaultListableBeanFactory:isAutowireCandidate(candidate,descriptor) Note over DefaultListableBeanFactory: 检查候选bean是否是合适的自动装配候选者 DefaultListableBeanFactory->>DefaultListableBeanFactory:isAutowireCandidate(beanName,descriptor,resolver) Note right of DefaultListableBeanFactory: 进一步检查,使用解析器 DefaultListableBeanFactory->>DefaultListableBeanFactory:isAutowireCandidate(beanName,mbd,descriptor,resolver) Note right of DefaultListableBeanFactory: 使用给定的bean定义进一步检查 DefaultListableBeanFactory->>QualifierAnnotationAutowireCandidateResolver:isAutowireCandidate(holder, descriptor) Note over QualifierAnnotationAutowireCandidateResolver: 判断是否根据@Qualifier注解是自动装配的候选者 QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:checkQualifiers(bdHolder,annotationsToSearch) Note over QualifierAnnotationAutowireCandidateResolver: 检查所有相关的限定符注解 QualifierAnnotationAutowireCandidateResolver->>QualifierAnnotationAutowireCandidateResolver:isQualifier(annotationType) Note over QualifierAnnotationAutowireCandidateResolver: 判断给定的注解类型是否是一个有效的限定符 ~~~ ### 七、源码分析 #### @Qualifier注册 首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`(此类是使用Java注解来配置Spring容器的方式),构造参数我们给定了一个`MyConfiguration`组件类。然后从Spring上下文中获取一个`MyController`类型的bean并调用了`showMessage`方法。 ```java public class QualifierApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class); MessageController messageController = context.getBean(MessageController.class); messageController.showMessage(); } } ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext`构造函数中,执行了三个步骤,我们本次重点关注`this()`。 ```java public AnnotationConfigApplicationContext(Class... componentClasses) { this(); register(componentClasses); refresh(); ``` 在`org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()`方法中,初始化了的两个核心组件,一个用于读取注解定义的bean (`AnnotatedBeanDefinitionReader`)也是本次重点分析的内容,另一个用于扫描类路径并自动检测bean组件 (`ClassPathBeanDefinitionScanner`)。 ```java 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); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(registry)`方法中,又调用了另一个构造函数。 ```java public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } ``` 在`org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(registry,environment)`方法中,这是一个重要的调用`registerAnnotationConfigProcessors`,会向容器注册一系列的后置处理器,这些后置处理器对于处理各种注解(如 `@Inject`, `@Qualifier`等)至关重要。 ```java 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); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry)`方法中,又调用了另外一个重载的方法。 ```java public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, null); } ``` 在`org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(registry,source)`方法中,在 Spring 的自动装配机制中,当有多个候选bean可以被注入到某个位置时,需要有一个方式来解决哪个bean是最佳的候选者。`AutowireCandidateResolver` 就是用来做这个决策的。 ```java public static Set registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { // ... [代码部分省略以简化] if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#QualifierAnnotationAutowireCandidateResolver()`方法中,目的是为解析器设置一些基本配置。其中最重要的部分是尝试加载 JSR-330 的 `@Qualifier` 注解,并将其添加到 `qualifierTypes` 集合中,以便在后续的依赖注入过程中能够正确处理这个注解。如果 JSR-330 不可用,解析器会简单地跳过这个步骤。 ```java public QualifierAnnotationAutowireCandidateResolver() { // ... [代码部分省略以简化] try { this.qualifierTypes.add((Class) ClassUtils.forName("javax.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader())); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } } ``` #### @Qualifier解析 ##### 前置条件 在Spring中,`AutowiredAnnotationBeanPostProcessor` 负责处理多种依赖注入注解,包括 JSR-330 的 `@Inject` 注解。为了深入了解 `@Inject` 注解及其与 `@Qualifier` 注解的结合方式,研究这个后处理器是至关重要的。简而言之,为了完全掌握 `@Inject` 和 `@Qualifier` 的协同工作原理,深入阅读 `@Inject` 博客是非常必要的。这些博客为我们提供了对如何在 bean 生命周期中的关键阶段处理这些注解的深入理解。 1. **@Inject注解源码分析**: - 在这篇博客中,我们会深入了解 `@Inject` 注解的背后原理。从 JSR-330 规范的引入,到如何在 Spring 中正确使用,以及与其他注解如 `@Autowired` 的差异,这篇博客为我们提供了全面的视角。 - 🔗 [@Inject注解传送门](https://github.com/xuchengsheng/spring-reading/blob/master/spring-jsr/spring-jsr330-inject) ##### 解析入口 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,首先尝试获取一个延迟解析代理。如果无法获得,它会进一步尝试解析依赖。`doResolveDependency` 是实际进行解析工作的方法。 ```java public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中,主要目的是解析给定的依赖描述符 (`DependencyDescriptor`),并尝试找到一个合适的 bean 来满足这个依赖。 ```java public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // ... [代码部分省略以简化] try { // ... [代码部分省略以简化] // 步骤1. 根据依赖描述符查找匹配的bean Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); // ... [代码部分省略以简化] } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates`方法中,首先基于给定的类型获取所有可能的bean名。接着,对于每一个可能的候选bean,它检查该bean是否是一个合适的自动注入候选,如果是,它将这个bean添加到结果集中。最后,方法返回找到的所有合适的候选bean。 ```java protected Map 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; } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#isAutowireCandidate(beanName, descriptor)`方法中,首先是调用 `getAutowireCandidateResolver()` 方法时,我们会得到这里设置的那个解析器,会根据其实现来决定哪个 bean 是自动装配的候选者,尤其当存在多个可能的候选者时,会考虑到 `@Qualifier` 和其他相关的注解,然后会进一步调用 `isAutowireCandidate` 的另一个重载版本,并且会传入解析器。 ```java @Override public boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor) throws NoSuchBeanDefinitionException { return isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver()); } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#isAutowireCandidate(beanName,descriptor,resolver)`方法中,首先是调用`getMergedLocalBeanDefinition(bdName)`来获取被依赖Bean的定义, 然后会进一步调用 `isAutowireCandidate` 的另一个重载版本,并且会传入合并的 bean 定义。 ```java 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); } // ... [代码部分省略以简化] } ``` 在`org.springframework.beans.factory.support.DefaultListableBeanFactory#isAutowireCandidate(beanName, mbd,descriptor, resolver)`方法中,首先是初始化了一个`BeanDefinitionHolder`,这是一个方便的数据结构,用于同时持有 `BeanDefinition`, bean 的名称和别名。这对于解析自动装配候选项很有用,因为有时我们需要知道 bean 的名称和别名,然后会进一步调用解析器的 `isAutowireCandidate` 方法,并且会传入`BeanDefinitionHolder`与依赖描述符 。 ```java 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); } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate`方法中,主要目的是增强了对 `@Qualifier` 注解的支持。如果一个bean或依赖被 `@Qualifier` 注解,并指定了一个bean的名称,那么只有名称匹配的bean会被认为是自动装配的候选者。如果没有指定名称,但指定了其他属性或元注解,那么只有匹配这些属性或元注解的bean会被认为是自动装配的候选者。 ```java @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; } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#checkQualifiers`方法中,主要目的是确保待注入的bean与所有相关的 `@Qualifier` 注解和元注解匹配。如果其中一个注解或元注解与bean定义不匹配,那么这个bean就不是当前注入点的合适候选。 ```java protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) { // 如果注解数组为空,直接返回 true if (ObjectUtils.isEmpty(annotationsToSearch)) { return true; } SimpleTypeConverter typeConverter = new SimpleTypeConverter(); for (Annotation annotation : annotationsToSearch) { Class 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 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; } ``` 在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#isQualifier`方法中,主要目的是确定给定的注解是否为一个"资格符"注解,也就是我们通常所说的限定符注解,如`@Qualifier`。这是通过检查注解是否存在于已知的`qualifierTypes`集合中,或者它是否带有这些资格符注解来完成的。 ```java protected boolean isQualifier(Class annotationType) { // 遍历已知的资格符注解类型 for (Class qualifierType : this.qualifierTypes) { // 如果给定的注解是已知的资格符注解,或者它带有这样的元注解 if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) { return true; // 是资格符注解 } } return false; // 不是资格符注解 } ``` ### 八、注意事项 1. **确保正确的依赖注入** + 使用 `@Qualifier` 主要是为了解决Spring容器中有多个同类型Bean的问题。没有 `@Qualifier`,Spring将无法决定注入哪一个Bean。但使用 `@Qualifier` 时,我们需要确保给定的资格符名称确实存在,否则Spring会抛出异常。 2. **与其他注解组合** + `@Qualifier` 通常与 `@Autowired` 或 `@Inject` 一起使用。确保在正确的地方使用它(字段、setter方法或构造函数)。 3. **自定义资格符注解** + 我们可以创建自己的注解,并使用 `@Qualifier` 作为元注解。这样,我们可以创建具有特定命名或其他语义的资格符注解,使代码更具可读性。 4. **使用字符串名称** + `@Qualifier` 默认使用字符串值来指定Bean的名称。这意味着,如果我们重命名了Bean或更改了其名称,我们也需要更改所有使用这个Bean名称的 `@Qualifier` 注解。 5. **与Java配置一起使用** + 当使用Java配置创建Beans时,可以使用 `@Bean` 的方法名称作为资格符名称。这使得使用Java配置和 `@Qualifier` 更为一致。 6. **与 `@Primary` 的关系** + 如果我们同时使用了 `@Primary` 和 `@Qualifier`,则 `@Qualifier` 的优先级更高。也就是说,如果一个Bean被标记为 `@Primary`,但在注入点使用了 `@Qualifier`,则会使用 `@Qualifier` 指定的Bean。 7. **名称和类型的匹配** + 尽管 `@Qualifier` 主要用于通过名称进行匹配,但Spring仍然会验证匹配的Bean类型。所以,如果Bean名称匹配但类型不匹配,仍然会出现异常。 8. **与JSR-330的兼容性** + Spring的 `@Qualifier` 注解与JSR-330(Java的依赖注入规范)中的 `javax.inject.Qualifier` 注解兼容。但是,当使用JSR-330标准时,确保依赖注入提供程序(如Spring)正确支持它。 ### 九、总结 #### 最佳实践总结 1. **明确的配置** + 使用 `AnnotationConfigApplicationContext` 作为Spring容器,并通过构造参数指定配置类,使得Spring容器初始化过程清晰明了。 2. **利用组件扫描** + 通过 `@ComponentScan` 注解,自动扫描指定包及其子包中的组件,减少了手动注册bean的工作,使代码更简洁、高效。 3. **自定义资格符注解** + 为了解决多个同类型Bean的问题,定义了 `@Email` 和 `@SMS` 两个自定义限定符注解。这使得代码更有可读性,并提供了更明确的注入意图。 4. **准确的服务注入** + 在 `MessageController` 中,使用了自定义的 `@Email` 和 `@SMS` 注解与 `@Inject` 组合,确保了正确的 `MessageService` 实现被注入到相应的字段。 5. **清晰的输出** + 通过 `showMessage` 方法的输出,我们可以清晰地看到 `@Qualifier` 的作用,确保了不同的服务实现被正确注入。 6. **充分利用Java配置** + 通过 `@Configuration` 和 `@ComponentScan`,结合Java配置,Spring容器的初始化和bean的注册过程都变得更加直观和简洁。 7. **注解的扩展性** + 通过使用 `@Qualifier` 作为元注解,自定义注解的方式体现了Spring的灵活性和注解的扩展性。 8. **保持代码整洁和模块化** + 每个类和接口都有明确的职责,并且代码被组织得清晰、模块化,这不仅使代码更易于维护,还提高了代码的可读性和可复用性。 #### 源码分析总结 1. **应用启动及上下文初始化** + 当创建`AnnotationConfigApplicationContext`时,它接收一个配置类(如`MyConfiguration`)作为参数,用于初始化Spring上下文。启动时,会从Spring上下文中获取并使用相应的Bean。 2. **AnnotationConfigApplicationContext的构造过程** + 在其构造函数中,`AnnotationConfigApplicationContext`执行了几个关键步骤,其中最核心的是`this()`,该方法初始化了两个组件:`AnnotatedBeanDefinitionReader`(负责读取注解定义的bean)和`ClassPathBeanDefinitionScanner`(负责扫描类路径并自动检测bean组件)。 3. **AnnotatedBeanDefinitionReader的初始化** + `AnnotatedBeanDefinitionReader`在其构造函数中调用`registerAnnotationConfigProcessors`,此方法向容器注册了一系列的后置处理器,这些后置处理器对于处理各种注解(如`@Inject`, `@Qualifier`等)是关键。 4. **Qualifier注解的注册** + 当创建`QualifierAnnotationAutowireCandidateResolver`实例时,它会尝试加载JSR-330的`@Qualifier`注解,并将其添加到`qualifierTypes`集合中。这使得在后续的依赖注入过程中,Spring可以正确处理这个注解。 5. **依赖解析过程** + 当Spring尝试解析某个依赖时,它会进入`DefaultListableBeanFactory#resolveDependency`。此方法首先尝试获取一个延迟解析代理,如果不能获得,则会尝试解析依赖,此过程涉及到`doResolveDependency`方法。 6. **自动装配候选检查** + 在`findAutowireCandidates`中,Spring首先找出所有可能的bean名称,然后检查每一个可能的候选bean,看看它是否是一个合适的自动装配候选。此检查涉及到`isAutowireCandidate`方法。 7. **利用AutowireCandidateResolver判断是否为自动装配候选者** + `isAutowireCandidate`方法的工作是判断某个bean是否是自动装配的候选者。在有多个可能的候选bean时,`AutowireCandidateResolver`(这里的实例是`QualifierAnnotationAutowireCandidateResolver`)会被用来做决策,考虑到`@Qualifier`和其他相关的注解。 8. **处理@Qualifier注解** + `QualifierAnnotationAutowireCandidateResolver`的主要任务是增强对`@Qualifier`注解的支持。它确保待注入的bean与所有相关的`@Qualifier`注解和元注解匹配。如果其中一个注解或元注解与bean定义不匹配,那么这个bean就不是当前注入点的合适候选。 9. **资格符检查** + `isQualifier`方法则用来确定给定的注解是否为一个资格符注解(限定符注解),例如`@Qualifier`。这是通过检查注解是否存在于已知的`qualifierTypes`集合中或它是否带有这些资格符注解来完成的。