How are Annotation Processors loaded?
Large projects can become complicated to build. One factor contributing to that is annotation processors. They can be loaded in many different ways. It can be hard to understand what Processors run and how seemingly unconnected changes affect that. I want to explain in this article how Annotation Processors are loaded and the potential pitfalls.
Java Compiler
Annotation Processors are loaded by the Java Compiler (javac). This Pseudocode demonstrates how.
public static List<AnnotationProcessor> getAnnotationProcessors(
String[] args,
List<AnnotationProcessor> programmaticallySetProcessors)
{ //source: javax.tools.JavaCompiler.CompilationTask#setProcessors(java.lang.Iterable)
//source: com.sun.tools.javac.main.JavaCompiler.initProcessAnnotations()
if (isOptionSet(args, "-proc:", "none") || !isRequested(args))
{
return Collections.emptyList();
}
return chooseProcessors(args, programmaticallySetProcessors);
}
//source: com.sun.tools.javac.main.JavaCompiler.explicitAnnotationProcessingRequested()
private static boolean isRequested(String[] args)
{
return isOptionSet(args, "-processor") ||
isOptionSet(args, "---processor-path") ||
isOptionSet(args, "--processor-module-path") ||
isOptionSet(args, "-proc:", "only") ||
isOptionSet(args, "-proc:", "full") ||
isOptionSet(args, "-A") ||
isOptionSet(args, "-Xprint") ||
hasLocation(ANNOTATION_PROCESSOR_PATH);
}
//source: com.sun.tools.javac.processing.JavacProcessingEnvironment.initProcessorIterator
private static List<AnnotationProcessor> chooseProcessors(
String[] args,
List<AnnotationProcessor> programmaticallySetProcessors)
{
if (isOptionSet(args, "-Xprint"))(1)
{
return List.of(new PrintingProcessor());
}
if (programmaticallySetProcessors != null)(2)
{
return programmaticallySetProcessors;
}
List<AnnotationProcessor> processors = loadProcessors();
List<String> processorNames = getOption(args, "-processor");(3)
if (processorNames != null)
{
return processors.stream()
.filter(p -> processorNames.contains(p.getName()))
.toList();
}
return processors;
}
//source: com.sun.tools.javac.processing.JavacProcessingEnvironment#initProcessorLoader())
private static List<AnnotationProcessor> loadProcessors()
{
if (hasLocation(ANNOTATION_PROCESSOR_MODULE_PATH))
{
return loadAnnotationProcessors(ANNOTATION_PROCESSOR_MODULE_PATH);(4)
}
if (hasLocation(ANNOTATION_PROCESSOR_PATH))
{
return loadAnnotationProcessors(ANNOTATION_PROCESSOR_PATH);(5)
}
return loadAnnotationProcessors(CLASS_PATH);(6)
}
Configurations
1 | -Xprint
With this command line Option a build in javac Annotation Processor is used to print everything it sees about a class. This can be very useful to get familiar with the code model of annotation processing. javac -Xprint HelloWorld.java
Annotation Processors can not access the content of methods. But default Constructors are present. result
|
2 | programmaticallySetProcessors
The Java compiler has a programmatic Api where Annotation Processors can be set.
|
3 | -processor
With this command line Option a List of Annotation Processors can be set via their name.
This is basically a filter for one of the other options or the classpath.
|
4 | --processor-module-path
The module path for loading Annotation processors |
5 | --processor-path or -processorpath
The path for loading Annotation processors. |
6 | --class-path
The path for user class files and Annotation Processors. |
Maven
During a build the build tool needs to invoke javac. This is how maven does it.
Without configuration
Configuration
-
-processor
can be set as <annotationProcessors> -
--processor-module-path
is not supported -
--processor-path
can be set as <annotationProcessorPaths> -
-proc
can be set as <proc> -
Per default maven doesn’t use the dependency management section to resolve transitive dependencies in Annotation Processors <annotationProcessorPathsUseDepMgmt>
Java Version Changes
Since java 23 Annotation Processing has to be explicitly requested and is no longer on per default. The official documentation is not all up to date.
Common Cases
Writing an Annotation Processor
Class-path scanning should be used with caution when writing a processor. The sources that are being compiled are on the class-path and javac will try to compile the Processor using itself.
Required Modularized Processors
If you want to use Annotation Processors to write your Processor both are modules and one requires the other, then the other processors will be put on the module-path and can not be automatically discovered. Some configuration is needed.