Spring supports injecting dependencies of single type and collection type. For a single type, if you inject by type, spring will throw a NoUniqueBeanDefinitionException when there are multiple beans of the same type in the container. In this case, we can choose to set a bean as primary. However, if there are multiple primary beans, spring will still be unable to process them. At this time, we will introduce @ Qualifier, which can be used to specify which bean to inject.
@Use of Qualifier annotations
@Qualifier annotations are usually used in two ways.
- When injecting a bean of a single type into a dependency, the name of the dependent bean is explicitly pointed out to avoid throwing exceptions when there are multiple beans of the same type.
- Group dependencies when injecting a collection type.
An example of injecting a single type bean is as follows:
public class App { @Qualifier("bean1") // ① @Autowired private String bean; public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(App.class); context.refresh(); System.out.println(context.getBean(App.class).bean); context.close(); } // @Qualifier("bean1") ② @Bean public String bean1() { return "bean1"; } @Bean public String bean2() { return "bean2"; } }
In the above example, two beans of type String are registered in the container. When injecting dependencies, use @ Qualifier to indicate that the name of the bean to be injected is bean1, so as to avoid throwing exceptions. Note that this is equivalent to adding @ Qualifier("bean1") to bean1, and the same purpose can be achieved by modifying position ① and position ② in the code to @ Qualifier("bean").
An example of using @ Qualifier to group dependencies for a collection type is as follows:
public class App { @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Qualifier public static @interface MyQualifierGroup{ } @Autowired private List<String> bean12; @Qualifier @Autowired private List<String> bean34; @MyQualifierGroup @Autowired private List<String> bean56; public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(App.class); context.refresh(); App app = context.getBean(App.class); System.out.println("bean12: "+app.bean12); System.out.println("bean34: "+app.bean34); System.out.println("bean56: "+app.bean56); context.close(); } @Bean public String bean1() { return "bean1"; } @Bean public String bean2() { return "bean2"; } @Qualifier @Bean public String bean3() { return "bean3"; } @Qualifier @Bean public String bean4() { return "bean4"; } @MyQualifierGroup @Bean public String bean5() { return "bean5"; } @MyQualifierGroup @Bean public String bean6() { return "bean6"; } }
In the above example, six String type beans are registered in the Spring container. Bean1 and bean2 are not annotated with @ Qualifier, bean3 and bean4 are annotated with @ Qualifier, and bean5 and bean6 are annotated with @ MyQualifierGroup using @ Qualifier. At the same time, three list < String > type dependencies are injected into the bean whose class type is App, Do not add @ Qualifier, add @ Qualifier and add @ MyQualifierGroup annotation respectively. The printing results are as follows:
bean12: [bean1, bean2, bean3, bean4, bean5, bean6] bean34: [bean3, bean4, bean5, bean6] bean56: [bean5, bean6]
Without @ Qualifier annotation, all bean s of the required type are injected. After @ Qualifier annotation is added, the dependent annotation must be consistent with the @ Qualifier type specified by us before injection.
@Simple analysis of Qualifier implementation
@Qualifier is used as annotation and processed by the context of annotation processing. AnnotatedBeanDefinitionReader} will read the annotation information as BeanDefinition. The construction method of AnnotatedBeanDefinitionReader is as follows:
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); // Registers the processor that handles annotations AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }
When instantiating, AnnotatedBeanDefinitionReader will call AnnotationConfigUtils#registerAnnotationConfigProcessors to register some beanpostprocessors processing annotations with Spring. The tracking source code is as follows:
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { // Register auto injected candidate parser beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } ... Omit some codes }
When registering the BeanPostProcessor, Spring will first register the automatically injected candidate resolver, contextannotation autowirecandidate resolver. The focus is on this resolver. When Spring resolves the dependency, it will call} DefaultListableBeanFactory#isAutowireCandidate to judge whether the bean of a given type is a dependency candidate. The tracking source code is as follows:
protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor, AutowireCandidateResolver resolver) { String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName); resolveBeanClass(mbd, beanDefinitionName); if (mbd.isFactoryMethodUnique && mbd.factoryMethodToIntrospect == null) { new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd); } BeanDefinitionHolder holder = (beanName.equals(beanDefinitionName) ? this.mergedBeanDefinitionHolders.computeIfAbsent(beanName, key -> new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName))) : new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName))); // Resolve whether the given bean is a candidate for automatic injection return resolver.isAutowireCandidate(holder, descriptor); }
The ContextAnnotationAutowireCandidateResolver set above is used here. This class will match the @ Qualifier on the bean with the @ Qualifier information in the dependency descriptor to group the beans.
Draught does not forget the well Digger: |