Backend Development 7 min read

Master Spring @Qualifier: Resolve Bean Ambiguity with Custom Annotations

This guide explains how Spring's @Qualifier annotation eliminates bean injection ambiguities, shows basic usage, custom qualifier creation, generic qualifiers, fully custom annotations, and advanced attribute‑based qualifiers, all illustrated with clear Java code examples for Spring 6.1.2.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring @Qualifier: Resolve Bean Ambiguity with Custom Annotations

1. Introduction

The @Qualifier annotation in Spring resolves ambiguity during autowiring when multiple beans of the same type exist. By specifying a qualifier, you explicitly tell the container which bean to inject.

Example with two CommonDAO beans:

<code>public class CommonService {
  @Resource
  @Qualifier
  private CommonDAO dao ;
}

@Configuration
public class AppConfig {
  @Bean
  @Qualifier
  public TeacherDAO teacherDAO() {
    return new TeacherDAO();
  }

  @Bean
  public StudentDAO studentDAO() {
    return new StudentDAO();
  }

  @Bean
  public CommonService commonService() {
    return new CommonService();
  }
}</code>

If the @Qualifier is omitted on the CommonDAO field, Spring will throw an error due to ambiguity.

2. More Uses

Beyond the basic qualifier, you can create custom qualifiers, use generics, or define fully custom annotations.

2.1 Custom Qualifier Annotation

<code>@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Pack {
  String value();
}</code>

Apply the custom qualifier:

<code>public class CommonService {
  @Resource
  @Pack
  private CommonDAO dao ;
}

@Bean
@Pack
public TeacherDAO teacherDAO() {
  return new TeacherDAO();
}</code>

2.2 Generic Qualifier Injection

<code>public class Teacher {}
public class Student {}
public interface CommonDAO<T> {}
public class TeacherDAO implements CommonDAO<Teacher> {}
public class StudentDAO implements CommonDAO<Student> {}

public class CommonService {
  @Resource
  private CommonDAO<Student> dao ;
  @Override
  public String toString() {
    return "CommonService [dao=" + dao + "]";
  }
}</code>

Injecting a CommonDAO&lt;Student&gt; will select StudentDAO ; using Teacher selects TeacherDAO .

List injection example:

<code>@Resource
private List<CommonDAO<Student>> daos ;</code>

This injects all beans whose generic type is Student .

2.3 Fully Custom Annotation (no Spring qualifier)

<code>@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pack {
  String value() default "";
}</code>

Register the custom qualifier with Spring:

<code>@Bean
public CustomAutowireConfigurer customAutowireConfigurer() {
  CustomAutowireConfigurer autowireConfigurer = new CustomAutowireConfigurer();
  // specify our custom annotation
  autowireConfigurer.setCustomQualifierTypes(Set.of(Pack.class));
  return autowireConfigurer;
}</code>

CustomAutowireConfigurer is a BeanFactoryPostProcessor that registers the custom qualifier.

2.4 Attribute‑Based Qualifier

<code>public enum Format { JSON, CSV, PLAIN }

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Pack {
  String value() default "";
  Format format();
}

@Pack(format = Format.CSV)
public class CSVDAO implements CommonDAO {}

@Pack(format = Format.JSON)
public class JSONDAO implements CommonDAO {}</code>

When registering these beans, you must set the format attribute via XML or programmatic bean definitions:

<code>ApplicationContext context = ... ;
context.registerBean(CSVDAO.class, bd -> {
  bd.setAttribute("format", "CSV");
});
context.registerBean(JSONDAO.class, bd -> {
  bd.setAttribute("format", "JSON");
});</code>

Inject with attribute matching:

<code>@Resource
@Pack(format = Format.JSON)
private CommonDAO dao ;</code>

Using the format attribute ensures that only beans with the matching metadata are injected.

backendjavaSpringCustom Annotationdependency injectionQualifier
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.