How are Annotation Processors loaded?
Annotation Processors can be loaded in many different ways. Some straight forward and some more indirect. In this article I want to document how the loading of Annotation Processors works and what Pitfalls there are.
Tldr: Configure them explicitly as <annotationProcessorPaths> or with this workaround for the jpms.
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, "-processorpath") ||//alias --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. javac -Xprint HelloWorld.java
Annotation Processors can not access the content of methods. But default Constructors are present. It’s an easy way to get familiar with the code mode an Annotation Processor can use. 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 Annotation Processors should be used if the processor and all dependencies are modules. |
5 | --processor-path or -processorpath
Should be used if an Annotation Processors or one of its dependencies is not a module |
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.