Fundamentals 13 min read

Understanding Java Annotations and Meta‑Annotations: Principles, Built‑in Annotations, and Custom Annotation Creation

This article explains the purpose and mechanics of Java annotations introduced in JDK 5, describes the five standard annotations, details the four meta‑annotations (@Target, @Retention, @Inherited, @Documented), and provides step‑by‑step guidance on defining, using, and processing custom annotations with reflection.

Java Captain
Java Captain
Java Captain
Understanding Java Annotations and Meta‑Annotations: Principles, Built‑in Annotations, and Custom Annotation Creation

All Java developers in China know that mainstream project architectures such as SSM, SSH, and Spring Cloud rely heavily on Spring, which uses a large number of annotations (including Spring‑specific ones). To use annotations effectively, one must understand the principles and basic usage of Java annotations.

JDK 5.0 introduced several impactful features, including enums, autoboxing/unboxing, generics, and annotations. Annotations were added to provide metadata support.
An annotation is an interface; programs can obtain an Annotation object for a given program element via reflection and retrieve the metadata stored in the annotation. Annotations do not affect program execution unless a tool (such as an Annotation Processing Tool) processes them.

JDK 5 also supplies five basic annotations:

@Override – forces a method to override a superclass method. @Deprecated – marks a class or method as outdated. @SuppressWarnings – suppresses compiler warnings. @SafeVarargs – suppresses heap‑pollution warnings for varargs. @FunctionalInterface – indicates that an interface is a functional interface (contains exactly one abstract method).

These built‑in annotations mainly provide compile‑time checks and do not change runtime behavior. Spring, however, introduces many more annotations that simplify repetitive coding tasks.

To create custom annotations, you first need to understand Java’s meta‑annotations, which are annotations that annotate other annotations.

Java defines four standard meta‑annotations: @Target – specifies the kinds of program elements an annotation can be applied to. @Retention – defines how long the annotation is retained (SOURCE, CLASS, or RUNTIME). @Inherited – indicates that an annotation is inherited by subclasses. @Documented – signals that the annotation should be included in Javadoc.

@Target

Purpose: Marks the applicable element types for an annotation.

Possible ElementType values:

CONSTRUCTOR – constructors FIELD – member variables LOCAL_VARIABLE – local variables METHOD – ordinary methods PACKAGE – packages PARAMETER – parameters TYPE – classes, interfaces, enums, annotation types ANNOTATION_TYPE – other annotations

Example:

@Target(ElementType.FIELD)
public @interface TargetTest6 {}

@Target({ElementType.TYPE_PARAMETER, ElementType.METHOD})
public @interface TargetTest7 {}

@Retention

Specifies how long the annotation is retained:

SOURCE – discarded by the compiler. CLASS – stored in the .class file but not available at runtime. RUNTIME – retained at runtime and accessible via reflection.
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetTest6 {}

@Inherited

When an annotation is marked with @Inherited, subclasses automatically inherit the annotation from their superclass.

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TargetTest6 {}
// All classes annotated with @TargetTest6 will have the annotation inherited by subclasses.

@Documented

Indicates that the annotation should be included in generated Javadoc.

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface TargetTest6 {}
// Javadoc will contain information about @TargetTest6.

With these meta‑annotations, you can define custom annotations. The syntax is similar to an interface:

public @interface MyAnnotation {
    // member variables are defined as parameter‑less methods
    String name();
    int age();
}

Example custom annotation with @Target and @Retention:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonTest {
    int age();
    String name();
}

Usage (values must be supplied unless defaults are defined):

public class Test {
    @AnonTest(age = 0, name = "1")
    public Integer tes;
}

If a member has a default value, the value can be omitted:

@Target(ElementType.FIELD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonTest {
    int age() default 1;
    String name() default "annotation default";
}
public class Test {
    @AnonTest // uses default values
    public Integer tes;
}

Annotations themselves do not execute code; you must retrieve and process them. Java provides the Annotation interface and the AnnotatedElement interface with methods such as getAnnotation, getAnnotations, isAnnotationPresent, and getDeclaredAnnotations.

Class – class definition Constructor – constructor definition Field – field definition Method – method definition Package – package definition

Example of extracting annotations via reflection:

@Component
@Service
public class Test {
    @AnonTest
    public Integer tes;

    @Deprecated
    public void m1() {}

    public static void main(String[] args) {
        try {
            Annotation[] classAnnotations = Class.forName("com.anon.test").getAnnotations();
            Annotation[] methodAnnotations = Class.forName("com.anon.test").getMethod("m1").getAnnotations();
            for (Annotation a : classAnnotations) {
                System.out.println(a.getClass());
                if (a instanceof Component) {
                    System.out.println("Found annotation:" + a);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Processing a custom annotation on fields:

public static void getInfo(Class
clazz) {
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        if (field.isAnnotationPresent(AnonTest.class)) {
            AnonTest anon = field.getAnnotation(AnonTest.class);
            System.out.println("Name: " + anon.name());
            System.out.println("Age: " + anon.age());
            System.out.println("Report to user age distribution table");
        }
    }
}

Running the above code produces:

1. Name is: 张小龙
2. Age is: 25
3. Report to user age distribution table

In summary, annotations are a supplemental mechanism in Java that do not affect program execution by themselves, but when used wisely they can greatly improve development efficiency and code standardization.

JavareflectionCustom AnnotationAnnotationsMeta‑annotations
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

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.