Annotation processors and compiler plugins often define annotations with source
or class
retention. These annotations are not exposed at runtime, thus there is no need to include them in the runtime classpath; they can be used via compileOnly
in a Gradle build file. Additionally, there is no need to declare their use in the module-info
file. Yet, in the presence of a module-info
file, the Java compiler requires the annotation classes to be included in the runtime classpath -- they must be declared in the module-info, which means they must be accessed from Gradle with implementation
instead of compileOnly
. This appears to be a hole in the compiler's support for JPMS. Or, is there a good explanation for this odd behavior?
Here's an example.
package com.example;
...
@Retention(RetentionPolicy.SOURCE)
public @interface Example {
...
}
The com.example.Example
annotation is defined in dependency my-annotation-proc
.
dependencies {
compileOnly 'com.example:my-annotation-proc:0.1-SNAPSHOT'
annotationProcessor 'com.example:my-annotation-proc:0.1-SNAPSHOT'
}
Usage of ExampleAnnotation
in Foo.java.
package abc;
public class Foo {
@com.example.Example
public void something() {
...
}
}
The module-info.java
file should not need a requires
for usage of the annotation.
module MyProject {
// Should be no need for this.
// Plus, adding it requires an `implementation` dependency in Gradle, which brings it into runtime where it does not belong.
//requires my.annotation.proc;
}
Compiling the project produces a compile error indicating the com.example
is not visible etc.
A requires
directive is mandatory for any dependency, not just runtime dependencies.
Compare with JLS, §7.7.1:
7.7.1. Dependences
The
requires
directive specifies the name of a module on which the current module has a dependence.…
The
requires
keyword may be followed by the modifierstatic
. This specifies that the dependence, while mandatory at compile time, is optional at run time.
I don’t know whether using requires static my.annotation.proc;
solves your problem with Gradle, but that’s how it is supposed to be handled at the language level.