Backend Development 8 min read

Inject Jar Version into Java Components with Compile‑Time Annotation Processors

This article demonstrates how to create a custom compile‑time annotation processor that automatically injects the jar version into Java component constants, enabling Prometheus monitoring of version usage without manual updates.

macrozheng
macrozheng
macrozheng
Inject Jar Version into Java Components with Compile‑Time Annotation Processors

Insertable annotation processors were mentioned in "深入理解Java虚拟机" and are now applied to a real scenario.

Requirement

The company provides a set of Java base component packages (circuit breaker, load balancing, RPC, etc.) packaged as JARs in an internal repository. They need to monitor the usage ratio of each version via Prometheus, as shown in the desired chart.

Problem

Manually adding a version constant to each component is cumbersome; a better solution is to inject the version at compile time, similar to Lombok, by using a custom annotation.

Define an empty constant with a custom annotation:

<code>@TrisceliVersion
public static final String version = "";</code>

The processor will replace the empty string with the actual version.

Solution

Java can resolve annotations at compile time or runtime. Lombok uses a source‑retention annotation, which is processed only during compilation via the Pluggable Annotation Processing API (JSR‑269). An insertable annotation processor can modify the abstract syntax tree (AST) at compile time.

Define the annotation:

<code>@Documented
@Retention(RetentionPolicy.SOURCE) // only at compile time
@Target({ElementType.FIELD})
public @interface TrisceliVersion {}
</code>

Define a processor extending

AbstractProcessor

:

<code>public class TrisceliVersionProcessor extends AbstractProcessor {
    private JavacTrees javacTrees;
    private TreeMaker treeMaker;
    private ProcessingEnvironment processingEnv;

    @SneakyThrows
    @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() {
        // simplified: return fixed value
        return "v1.0.1";
    }
}
</code>

The processor must be discovered via the SPI mechanism by adding a

META‑INF/services

entry.

Test

Create a test module, include the processor, and run a Gradle build. The generated bytecode now contains the version value.

Result screenshot shows the field populated with the version.

Conclusion: Insertable annotation processors can modify the AST during compilation, enabling powerful plugins like Lombok and allowing developers to create creative compile‑time code generation solutions.

Javacode generationGradlePrometheusannotation-processingLombokcompile-time
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.