-
Notifications
You must be signed in to change notification settings - Fork 24
Description
A lot of folks are not aware of this but you should almost never use the actual annotation classes in an annotation processor even your own. BTW I only know of couple libraries that generally do this correctly: MapStruct, JStachio (mine) and I believe Micronaut (so don't feel bad). It is also probably why no one knows about Hickory (or the new MapStruct gemtools).
So why is it problematic? Well the annotation processor is not guaranteed to have classes loaded and if your annotation references other classes (e.g. has a Class<?>
parameter ) that are not yours you will have issues especially and I mean especially if you are dealing with modularized projects.
So why have you guys not run into issues? Well because both Maven and I think gradle as well will simply move whatever dependencies that are in <annotationProcessorPaths>
into the classpath during compilation. Otherwise all libraries that are referenced with requires
(static or not) in module-info
(and not in the annotationProcessorPaths) will be put in the module-path.
The above is one the reasons why <annotationProcessorPaths>
is required for modular projects but it is seriously limited and technically not correct either: https://issues.apache.org/jira/browse/MCOMPILER-391 and https://issues.apache.org/jira/browse/MCOMPILER-412
Otherwise if avaje-inject-generator does not have dependencies just having avaje-inject-generator on the classpath will work and you would not need to do <annotationProcessorPaths>
for modular projects and this is one of the big advantages to doing it the painful. BTW this is also why lots of annotation processors shade their deps but you can't really do that for annotations.
So how do you access your own annotations without using them directly? You have to load the TypeElement.
avaje-inject/inject-generator/src/main/java/io/avaje/inject/generator/Processor.java
Line 118 in 12eba7f
readScopes(roundEnv.getElementsAnnotatedWith(Scope.class)); |
Instead of Scope.class
you are going to pass a TypeElement. To get the TypeElement you can either get it from the call process function or just call Elements.getTypeElement
: https://docs.oracle.com/en/java/javase/11/docs/api/java.compiler/javax/lang/model/util/Elements.html#getTypeElement(java.lang.CharSequence)
Anyway that is easy and fine however it becomes a pain in the ass to get annotation parameters. That is where Hickory comes in (or mapstruct gemtools but you would have to shade it).
https://javadoc.io/static/com.jolira/hickory/1.0.0/net/java/dev/hickory/prism/package-summary.html
Hickory is an annotation processor that makes what are called prisms which you can access your annotations as though you are accessing them directly.
Anyway I normally would not bother even bringing all this up as yeah there is a work around ... manually register annotation processors...
But the project appears to not heavily dependent on its annotation core lib and is not a big change. In fact I could probably do a PR and do it myself if you are interested.
Furthermore it is technically the right way to do it and will prevent problems in the future as more folks embrace modularity.