How do you configure annotation processors in the maven-compiler-plugin, and why is annotationProcessorPaths preferred over putting processors on the classpath?
answer
- processor-path vs class-path
- no leakage into dependency tree
- Lombok / MapStruct / Dagger
- -A options via compilerArgs
- ordering matters for combined processors
basics
~10 sUse <annotationProcessorPaths> to list processor artifacts (like Lombok or MapStruct) separately from your project dependencies. This keeps processors off the runtime/compile classpath while still running them during compilation.
solid answer
~40 sAnnotation processors (Lombok, MapStruct, Dagger, auto-value) generate code at compile time. The old approach was adding them as `provided` dependencies so javac discovers them on the classpath via the ServiceLoader. The modern approach is `<annotationProcessorPaths>` in the compiler plugin: it puts processors on the dedicated `--processor-path` so they run during compilation but never leak into your project's transitive dependency graph or runtime classpath. This avoids version conflicts (a processor's own deps don't collide with yours) and makes the dependency tree honest. You can also pin `<annotationProcessors>` to name exact processor classes, and pass options via `<compilerArgs>` (e.g. `-Amapstruct.defaultComponentModel=spring`). With multiple processors that depend on each other (Lombok + MapStruct), ordering inside annotationProcessorPaths matters.
code
xml · 12 lines<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.6.3</version>
</path>
</annotationProcessorPaths>go deeper
Knows tools like Lombok generate code at build time and are configured in the compiler plugin.
Can add an annotationProcessorPaths entry and locate generated sources under target/generated-sources.
Explains why processor-path beats classpath (no leakage/conflicts) and passes -A options correctly.
Governs processor versions centrally, manages Lombok+MapStruct ordering/binding, and keeps the dependency graph free of compile-only tooling.
## What annotation processors are An **annotation processor** is a plugin to `javac` (implementing `javax.annotation.processing.Processor`) that runs during compilation and can read annotations and **generate new source/class files**. Examples: Lombok (`@Getter`), MapStruct (mapper impls), Dagger (DI graph), Google AutoValue. ## Two ways to give javac a processor 1. **On the compile classpath** (old way): declare the processor as a `<scope>provided</scope>` dependency. javac auto-discovers it via `META-INF/services/javax.annotation.processing.Processor`. 2. **On the processor path** (preferred): list it under `<annotationProcessorPaths>` in the compiler plugin. This maps to javac's `--processor-path`, which is **separate** from `--class-path`. ## Why annotationProcessorPaths is better - **No leakage**: the processor and *its* transitive dependencies never enter your project's dependency tree, so `mvn dependency:tree` stays clean and nothing ships at runtime. - **No conflicts**: a processor bringing, say, an old Guava won't clash with your app's Guava. - **Explicit & reproducible**: you state exactly which processors run. ## Configuration ```xml <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.6.3</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.34</version> </path> </annotationProcessorPaths> <compilerArgs> <arg>-Amapstruct.defaultComponentModel=spring</arg> </compilerArgs> </configuration> </plugin> ``` ## Related knobs - `<annotationProcessors>` — explicit list of processor **class names** to run (disables auto-discovery; useful to force order or restrict which run). - `-A<key>=<value>` options passed through `<compilerArgs>` configure the processors. - `<generatedSourcesDirectory>` — where generated sources land (default `target/generated-sources/annotations`); Maven adds it as a source root automatically. ## Ordering gotcha When Lombok and MapStruct are combined, Lombok must process first so MapStruct sees the generated getters/setters. With annotationProcessorPaths the order of `<path>` entries controls this; the lombok-mapstruct-binding artifact is the robust fix.
- What does annotationProcessorPaths map to at the javac level?The --processor-path argument, which is separate from --class-path, so processors and their transitive deps don't pollute the project classpath.
- How do you pass an option to a processor, e.g. MapStruct's component model?Add -A<key>=<value> inside <compilerArgs>, e.g. -Amapstruct.defaultComponentModel=spring.
saying these in an interview costs you the question
- Insisting processors must be 'provided' classpath dependencies — outdated and leaks transitive deps.
- Claiming processors run at runtime — they run only at compile time.
- Forgetting that processor ordering can matter when combining Lombok and MapStruct.