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.)