Search code examples
javaannotationscdiweld

Why does Weld need a AnnotationLiteral instead just of the Annotation class?


I just tried to retrieve an object from CDI with a Qualifier.

private static class QualifierLiteral
        extends AnnotationLiteral<RandomQualifier>
        implements RandomQualifier {
}
QualifierLiteral QUALIFIER = new QualifierLiteral();
CDI.current().select(TranslationProvider.class, QUALIFIER).get()

I wonder, whats the reason why its not RandomQualifier.class here?

Is there any reason, why they need the instance of a literal instead of just the class?

(Scope of the question is to learn something about the inner workings of weld/cdi/java)


Solution

  • Weld supports qualifiers with members (the annotation elements). If you could only pass Class arguments, it wouldn't be able to do that. Instead, select expects an annotation instance that can provide expected values for those members.

    Remember that annotation types are just interfaces. So you can create subclasses for them and instantiate those. For example, assume your RandomQualifier looked like

    @interface RandomQualifier {
        int randomNumber();
    }
    

    You could have

    class RandomQualifierImpl implements RandomQualifier {
    
        @Override
        public Class<? extends Annotation> annotationType() {
            return RandomQualifier.class;
        }
    
        @Override
        public int randomNumber() {
            return 4;
        }
    }
    

    Normally, when you annotate a class (or anything else) with your annotation and use reflection to extract that annotation, the JVM is responsible for creating a dynamic subclass that implements those methods and returning a corresponding instance. But here, we have to do it ourselves.

    For convenience, the CDI framework provides AnnotationLiteral, as a helper class, to provide the implementation of annotationType() using the type token "hack".

    This way, you can simply write

    class RandomQualifierImpl extends AnnotationLiteral<RandomQualifier> implements RandomQualifier {
        @Override
        public int randomNumber() {
            return 4;
        }
    }
    

    and provide a new RandomQualifierImpl instance to select.

    Or, if you don't need annotation elements (members) at all, use an anonymous subclass

    Annotation instance = new AnnotationLiteral<RandomQualifier>() {};