Fundamentals 12 min read

Understanding Java Annotations: Definition, Essence, Details, and Parsing SOURCE‑Retention Annotations

This article explains what Java annotations are, their underlying nature as interfaces extending Annotation, the various meta‑annotations such as @Retention and @Target, how retention policies affect availability, and demonstrates how to parse SOURCE‑retention annotations using a custom AbstractProcessor with complete code examples.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Understanding Java Annotations: Definition, Essence, Details, and Parsing SOURCE‑Retention Annotations

In Java development you often see @xxx placed on classes or methods; this is called an annotation . The article starts with a simple test method and shows the custom annotation @MyRequiredArgsConstructor and its usage on a class ExamplePo .

What Is an Annotation

Annotations provide extra information to the compiler and can be applied to classes, methods, fields, parameters, etc. They act like tags that tell the compiler to perform specific actions during compilation or runtime.

The Essence of Annotations

An annotation is essentially an @interface which, after compilation, becomes a regular interface that extends java.lang.annotation.Annotation . The example shows the compiled form of MyRequiredArgsConstructor and how the @interface is transformed.

Annotation Details

Annotations are read by the compiler; if the compiler does not process them they are meaningless. They can be retained at different stages:

RetentionPolicy.SOURCE : kept only in source code.

RetentionPolicy.CLASS : kept in the compiled class file.

RetentionPolicy.RUNTIME : available at runtime via reflection.

The @Target meta‑annotation restricts where an annotation can be placed (e.g., TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE).

Meta‑Annotations

Common meta‑annotations include @Retention , @Target , @Documented , and @Inherited , which control lifecycle, scope, documentation, and inheritance of annotations.

Parsing SOURCE‑Retention Annotations

Because SOURCE annotations disappear after compilation, they cannot be accessed via reflection. The article provides a custom annotation processor that extends AbstractProcessor to read such annotations at compile time. The processor scans elements annotated with MyRequiredArgsConstructor , reflects on the class, and generates a constructor that includes all final or annotated fields.

@SupportedAnnotationTypes("com.example.annotation.MyRequiredArgsConstructor")
@Slf4j
public class SourceAnnotationProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set
annotations, RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(MyRequiredArgsConstructor.class)) {
            Name qualifiedName = ((TypeElement) element).getQualifiedName();
            Class clazz = null;
            try { clazz = Class.forName(qualifiedName.toString()); }
            catch (ClassNotFoundException e) { throw new RuntimeException(e); }
            // generate constructor code based on fields and annotation attributes
            // ... (code omitted for brevity)
            log.info("generated ExamplePo Construct: \n [{}]", constructor);
        }
        return true;
    }
}

A test method demonstrates how to invoke the Java Compiler API, register the processor, and compile a source file, producing the generated constructor.

@Test
public void testAnnotationDemo() {
    // pseudo‑code to compile ExamplePo.java with the custom processor
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
    Iterable
compilationUnits = fileManager.getJavaFileObjectsFromFiles(
        Arrays.asList(new File("src/test/java/com/example/ExamplePo.java")));
    JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits);
    task.setProcessors(Arrays.asList(new SourceAnnotationProcessor()));
    task.call();
}

The output shows that the processor successfully detected the annotation and generated the corresponding constructor.

Conclusion

If an annotation uses @Retention(SOURCE) , it is unavailable at runtime; to access it you must either change the retention to CLASS or RUNTIME , or use a compile‑time processor as demonstrated. Alternatively, bytecode manipulation tools like ASM or Byte Buddy can embed source‑level annotation data into the class file.

backendJavareflectionAnnotationsannotation-processingMeta‑annotationsSource Retention
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

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.