spring-bootdependency-injectionspring-annotationsqualifiers

Typesafe @Qualifier for Spring Boot to avoid errors with strings


I was asked (and asked myself) many times, what is the best way to avoid error-prone strings in the Spring Boot @Qualifier annotation. (And of course other annotations.)

What is better than spreading @Qualifier("variantABC") or @Qualifier("variantCBA") all over the code? For example, in Spring Batch, I have qualifiers to distinguish between different jobs and their steps that are used at least three or four times.

Is using interfaces or classes with (just) constant strings really the [only|best|cleanest] solution?

public class Variants{
  public final static String VARIANT_ABC = "variantABC";
  public final static String VARIANT_CBA = "variantCBA";
}

Or can we do better?


Solution

  • I found out, the best solution for me was to use my own annotations instead of using annotations with strings.

    Why not use strings?

    Strings in annotations have the obvious disadvantage, that you can easily make typos at one or the other usage. And it could be hard to find the error.

    And it is just not clean to have redundant strings all over the code.

    Why not use constant interfaces or constant classes?

    Why you should not use the constant interface pattern you can read here: https://en.wikipedia.org/wiki/Constant_interface

    And even using a constant class as in the question is not a solution since it requires one place which knows all possible annotations. That doesn't sound like decoupling and clean code either.

    A solution with an own annotation

    For each @Qualifier("....") annotation which I use more than once, I create a small annotation on my own:

    @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
    @Inherited
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier("variantABC")
    public @interface VariantABC {}
    

    This way I have to enter the string VariantABC only once and cannot mistype it.

    I can simply use the annotation instead of the Qualifier annotation on bean declarations:

    @Bean
    @VariantABC
    Foo getImplementationVariantABC() {...}
    

    or all types of injections:

    public MyService(@VariantABC Foo foo) {...}
    
    // or
    
    @Autowired
    @VariantABC 
    Foo foo;
    

    (@Target, @Inherited and @Documented and @Inherited are optional. Read the Javadoc for more information of their purpose.)