Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Sessions intra-entreprises sur demande : contact[at]mokatech.net.
Inscrivez-vous vite !

Introducing AnnotationInjector

Let me introduce you to AnnotationInjector, a small library that allows to inject annotations on classes at runtime.
It began as sample code for the conference on annotations I presented at several JUGs, and was extracted later as a standalone library for general consumption.

What does it do ?

AnnotationInjector allows you to inject annotations on classes at runtime.
It works only on classes though, not on fields, methods, or parameters, due to some gory implementation details in the Class class on the Sun/Oracle JRE.

Let's see it in action :

// Pojo does not bear the MyAnnotation annotation
MyAnnotation existingAnnot = Pojo.class.getAnnotation(MyAnnotation.class);
assertNull(existingAnnot);
 
// Instanciate and inject a new MyAnnotation on Pojo
MyAnnotation injectedAnnot = new MyAnnotation() { ... };
AnnotationInjector injector = new AnnotationInjector(Pojo.class);
injector.injectAnnotation(injectedAnnot);
 
// Now Pojo bears our new annotation
existingAnnot = Pojo.class.getAnnotation(MyAnnotation.class);
assertNotNull(existingAnnot);
assertEquals(existingAnnot, injectedAnnot);

AnnotationInjector and InheritedAnnotationsInjector

The AnnotationInjector library comes with two injectors : the basic AnnotationInjector, and the InheritedAnnotationsInjector.

The former takes care of the nuts and bolts of annotation injection, as shown in the example above, including conflicts resolution.
The latter is an extension of the former, and is specialized in finding and injecting annotations that can be "inherited" (that is, annotations bearing the @Inherited meta-annotation) from the interfaces the target class implements, and from its package.

A word on @Inherited annotations :

As you may know, the @Inherited meta-annotation describes annotations that are visible to subclasses of the annotated class.
For example :

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface MyAnnotation {}
 
@MyAnnotation
class SuperClass {}
 
class Subclass extends SuperClass {}
 
boolean isMyAnnotationInherited = Subclass.class.isAnnotationPresent(MyAnnotation.class); // returns true

However, it does not work when the inheritable annotation is put on an interface, due to potential conflicts : a given class may implement multiple interfaces bearing the same annotation, but with different, conflicting parameters.

This is where the InheritedAnnotationsInjector comes in. Thanks to its conflict detection ability, the InheritedAnnotationsInjector can gather @Inherited annotations on a class' interfaces and packages and inject them safely.
In case of a conflict, an AnnotationInjectionException is raised, with a nicely detailed message :

// When incompatible annotations are detected before injection :
Annotation conflict : annotation Foo is used on interfaces Bar and Baz with different parameters.

// Annotation already exists on the target class, with incompatible parameters :
Annotation Foo cannot be injected in class Bar because it already exists with different parameter values.

For more information, please refer to the InheritedAnnotationsInjector's injectPackageInheritedAnnotations() and injectInterfaceInheritedAnnotations() methods.

What use-cases does it address ?

Short answer : I don't know yet :)

I see it more as a technical demo than a production-ready library, but I am sure someone will end up finding a good use-case for it.
Here are some ideas I think may be worth exploring :

  • Registering arbitrary POJOs with containers (embedded Java EE containers, Spring...)
  • Mapping frameworks could be another good candidate (java-to-XML mappers for example).
  • Using injected annotations as pointcuts for aspect-oriented programming.

One interesting point to notice is that you can inject annotations on any class, including JDK core classes and closed-source third-party classes !
Imagine you could register String as an EJB, or SimpleDateFormat as a thread-scoped Spring bean...

How does it work ?

In the Class class, annotations are stored in a simple map (Map<Class<? extends Annotation>, Annotation>). Using reflection, it is possible to modify the map to inject new annotations. Easy as pie.
There are a few tricks indeed, but nothing too complicated. Take a look at the source code !

How can I use it ?

The AnnotationInjector library is freely available on GitHub :
https://github.com/OlivierCroisier/AnnotationInjector.

It has no external dependencies, and can be included in you projects right away.
However, it relies heavily on the particular implementation of the Class class provided in the Sun / Oracle JRE, so it may not work on alternative platforms (not tested).

Contributions and feedback appreciated !

If you like this library, find innovative use-cases or use it in your projects, please drop me a word !


Commentaires

1. Le mercredi 2 mars 2011, 11:31 par laurentforet

Les Bean Validation peuvent être aussi un bon use case. Ca permettrait de pouvoir rajouter/supprimer des contraintes de validation dynamiquement.

2. Le mercredi 3 avril 2013, 07:29 par Raja Nagendra Kumar

This is the one I am looking for to define all the DAO name queries.
@NamedQuery(name = ActivityRecovery.NAMED_QUERY_BY_PREFIX + "Id",
query = ActivityRecovery.SELECT_QUERY_WHERE + ".id = :id",
lockMode = READ),

this way I can elimate all the template code related to DAO as the fields keep change.

Regards,
Raja Nagendra Kumar,
C.T.O
www.tejasoft.com
-Java Code Quality Engineering

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.