How Do Different Retention Policies Affect My Annotations

How do different retention policies affect my annotations?

  • RetentionPolicy.SOURCE: Discard during
    the compile. These annotations don't
    make any sense after the compile has
    completed, so they aren't written to
    the bytecode.

    Example: @Override, @SuppressWarnings

  • RetentionPolicy.CLASS: Discard during
    class load. Useful when doing
    bytecode-level post-processing.
    Somewhat surprisingly, this is the
    default.

  • RetentionPolicy.RUNTIME: Do not
    discard. The annotation should be
    available for reflection at runtime.
    Example: @Deprecated

Source:
The old URL is dead now
hunter_meta and replaced with hunter-meta-2-098036. In case even this goes down, I am uploading the image of the page.

Image (Right Click and Select 'Open Image in New Tab/Window')
Screenshot of Oracle website

RetentionPolicy CLASS vs. RUNTIME

both may be accessed at the run-time anyway.

That's not what the javadoc says:

RUNTIME: Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.

CLASS: Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.

In practice, I'm not aware of any use-cases for CLASS. It would only be useful if you wanted to read the bytecode programmatically, as opposed to via the classloader API, but that's a very specialised case, and I don't know why you wouldn't just use RUNTIME.

Ironically, CLASS is the default behaviour.

Java Annotations - looking for an example of RetentionPolicy.CLASS

Of all of the large number of libraries I have in my current project. the only examples I can find are in the Google Guava library, for example com.google.common.annotations.GwtCompatible.

I'm not really sure why they chose this retention policy, though - perhaps for tools support, where the tools read the class files themselves, rather than going through the reflection API. I'm not sure that I really see the point of this distinction, though.

Configuring Annotations to be RUNTIME Retention By Default (without individual @Retention)

The Java Language Specification, section 9.6.4.2. @Retention states:

If T does not have a (meta-)annotation m that corresponds to java.lang.annotation.Retention, then a Java compiler must treat T as if it does have such a meta-annotation m with an element whose value is java.lang.annotation.RetentionPolicy.CLASS.

As you can see, it is a strict requirement that missing annotation is same as @Retention(RetentionPolicy.CLASS).

If you need @Retention(RetentionPolicy.RUNTIME), then you must explicitly specify that.

Can't see annotations via reflection despite RetentionPolicy being RUNTIME

Due to the @PreAuthorize annotation you don't get the actual class but a proxied instance of that class. As annotations aren't inherited (by design in the language) you won't see them.

I suggest to do 2 things, first use the AopProxyUtils.ultimateTargetClass to get the actual class of the bean and second use the AnnotationUtils to get the annotation from the class.

Map<String, Object> beans = appContext.getBeansWithAnnotation(RestController.class);
for (Map.Entry<String, Object> entry : beans.entrySet()) {
Class clazz = AopProxyUtils. AopProxyUtils.ultimateTargetClass(entry.getValue());
ReflectionUtils.doWithMethods(clazz, new MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Annotation[] annotations = AnnotationUtils.getAnnotations(method);
for(Annotation annotation : annotations) {
LOG.debug("Annotation: " + annotation);
}
}
});
}

Something like that should do the trick, also some cleanup using the Spring provided utility classes.

Explanation about Annotations

Annotations are more flexible in terms
of how you use them, with options for
whether the annotation information is
to be included in class files output
by the compiler and made available to
the application at run time

This, I think, refers to the fact that Java5 annotations can be dropped by the compiler, whereas some can be retained in the bytecode. This is controlled by the @Retention annotation that is placed on your annotation type, e.g

@Documented
@Retention(value=RUNTIME)
public @interface Deprecated

This indicates that the @Deprecated annotation will be present in the bytecode, and will also be visible to reflection. java.lang.annotation.RetentionPolicy defines the different options.



Related Topics



Leave a reply



Submit