Suppose I have this annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Name
{
String value();
}
This is going to be used as follows
@Name("name1")
public static Foo foo = new Foo();
I have multiples of these across my project source files. Is there an fairly simple way to search and collect all those “foo”s that’re preceded by @Name?
In other words, I’d like to write a method that would return a Set<Foo> containing these.
Thanks!!!
I am not really familiar with the classpath scanners that others are suggesting. They seem like a robust – if not ideal – solution.
If you have control over the source, then you could use annotation processing.
Create an annotation processor that will create a class –
MapClasswith a static memberMap<String,Foo>. Every time the annotation processor encounters the @Name annotation, it adds that to the source code ofMapClass. When it finishes processing the annotations, it will have the same effect as if you hard coded the map.Annotation processing happens during compile time. If some of the classes in your project are not compiled by you. For example, if someone else compiles some classes and gives a jar to you, then it won’t work as easily. But if all the classes are compiled by you then it should not be a problem.
To create an annotation processor, extend
AbstractProcessor. You will want to annotate your class with a@ SupportedAnnotationTypes ( "Name" )annotation (make sure name is the fully qualified name of your annotation.Override the
processmethod.processhas two parameters:annotationsandroundEnv.annotationsis just the set of annotations that this particular processor supports – in your case it should be (Name).roundEnvis a useful utility class.Iterate through the one annotation in
annotations. UseroundEnvtogetElementsAnnotatedWith. This should give you the set of all elements that carry the@Nameannotation.AbstractProcessorhas another utility member –processingEnv. Use itsgetFilermethod tocreateSourceFile.Then you have to modify your compilation a little bit. You must compile your processor separately and before the other classes. After the processor is compiled and you are compiling the other classes you must tell the compiler about your processor. If you are using the command line you would add
-processorpath /path/to/processor/class[es]and-processor qualified.name.of.processor.The advantages of this approach over the class path scanner is that everything happens at compile time. So for example, if you accidentally add a
@Nameannotation to aBarelement, then you can have the processor throw a compile time error (if you wish the processor can ignore it). Then you can fix it before the product ships. With a class path scanner, any error thrown is a run time error – which the user will see.The disadvantage of this approach is also that everything happens at compile time. This makes it harder to dynamically add classes to the project.