The constant-annotation-marker interface is used to mark the ahead-of-time allocated
annotation proxy objects. We re-use the
java.lang.Override
interface for
this purpose. Although this may seem like a strange choice at first it is necessary to avoid
the restrictions around the creation of proxy objects. We need an interface that can be used
to mark existing proxy objects by extending the list of interfaces they implement. We do this
in AnnotationObjectReplacer#replacementComputer(Object). We call
Proxy.newProxyInstance(ClassLoader, Class[], InvocationHandler)
passing in the same
class loader that loaded the class of the original proxy object, the extended interface list
and the original proxy object invocation handler. If the original proxy object is a proxy to
an annotation loaded by the boot class loader then the marker interface needs to be loaded by
the boot class loader too. This is imposed by
Proxy
API. Therefore, the
Override
interface gives us access to an interface that is already loaded by the boot
class loader. It also has the advantage that it declares SOURCE retention policy, so this
interface should not be otherwise present in the bytecode and no other uses should interfere
with our mechanism.
Note: Ideally we would use a custom marker interface. However, this is impossible as of JDK9
since the set of boot modules is fixed at JDK build time and cannot be extended at runtime.
This allows us to create an optimized type for the ahead-of-time allocated annotation proxy
objects which removes the overhead of storing the annotation values in a HashMap. See
AnnotationSupport.getSubstitution(ResolvedJavaType)
for the logic where this
substitution is implemented. This is possible since the ahead-of-time allocated annotation
proxy objects are effectively constant. The constant-annotation-marker interface is removed
before runtime. See
AnnotationSubstitutionType.getInterfaces()
. Therefore the
annotation proxy objects only implement the annotation interface, together with
Proxy
and
Annotation
, as expected.
The run-time allocated annotations use the default JDK implementation.
The downside of having a separate (more efficient) implementation for ahead-of-time allocated
annotations is that at run time there can be two proxy types for the same annotation type and
an equality check between them would fail.