How to Auto‑Inject JAR Version Numbers with a Custom Java Annotation Processor
This article explains how to create a compile‑time annotation processor that automatically injects the JAR version into a static constant, enabling Prometheus to monitor version usage without manual updates, and demonstrates the implementation with Lombok‑style code examples.
Requirement
Our company provides a generic Java component library (circuit‑breaker, load‑balancer, RPC, etc.) packaged as JARs and published to an internal repository. We want to monitor the usage ratio of each version with Prometheus, as shown in the diagram.
Problem
Manually writing the JAR version into a constant or configuration file for every component is error‑prone and must be updated on each release.
We would like Gradle to obtain the JAR version during the build and inject it automatically, similar to how Lombok generates getters and setters via an annotation.
Solution
Define a custom annotation @TrisceliVersion and an insert‑style annotation processor that runs at compile time (JSR‑269 / Pluggable Annotation Processing API). The processor reads the version information and replaces the constant’s initializer in the abstract syntax tree.
Annotation definition
@Documented
@Retention(RetentionPolicy.SOURCE) // only present at compile time
@Target({ElementType.FIELD})
public @interface TrisceliVersion {}Processor implementation
public class TrisceliVersionProcessor extends AbstractProcessor {
private JavacTrees javacTrees;
private TreeMaker treeMaker;
private ProcessingEnvironment processingEnv;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.processingEnv = processingEnv;
this.javacTrees = JavacTrees.instance(processingEnv);
Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
this.treeMaker = TreeMaker.instance(context);
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> set = new HashSet<>();
set.add(TrisceliVersion.class.getName());
return set;
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement t : annotations) {
for (Element e : roundEnv.getElementsAnnotatedWith(t)) {
JCTree.JCVariableDecl jcv = (JCTree.JCVariableDecl) javacTrees.getTree(e);
String varType = jcv.vartype.type.toString();
if (!"java.lang.String".equals(varType)) {
printErrorMessage(e, "Type '" + varType + "' is not support.");
}
jcv.init = treeMaker.Literal(getVersion());
}
}
return true;
}
private void printErrorMessage(Element e, String m) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, m, e);
}
private String getVersion() {
// In a real implementation this would read the actual JAR version.
return "v1.0.1";
}
}The processor is discovered via the SPI mechanism by adding its fully‑qualified name to META-INF/services/javax.annotation.processing.Processor.
Testing
Create a test module that depends on the component library, annotate a static final String field with @TrisceliVersion, build with Gradle, and the generated bytecode contains the injected version value.
This demonstrates how insert‑style annotation processors can modify the AST to produce customized bytecode, opening the door to many powerful compile‑time plugins.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
