Backend Development 22 min read

Master Java Annotations: From Basics to Custom Usage

This article provides a comprehensive guide to Java annotations, covering built‑in annotations, meta‑annotations, retention policies, repeatable annotations, and how to create and apply custom annotations with reflection and AOP for practical use cases.

Architecture & Thinking
Architecture & Thinking
Architecture & Thinking
Master Java Annotations: From Basics to Custom Usage

1 Java Annotation Basics

Annotations were introduced in JDK 1.5 to add metadata to packages, classes, interfaces, fields, method parameters, and local variables. Their main purposes are generating Javadoc, compile‑time checking, compile‑time dynamic processing, and runtime dynamic processing.

Generate Javadoc documentation by marking metadata.

Compile‑time checks using metadata for validation.

Compile‑time dynamic handling such as code generation.

Runtime dynamic handling via reflection to inject instances.

Annotations are classified into three categories:

Standard Java annotations – e.g., @Override, @Deprecated, @SuppressWarnings.

Meta‑annotations – annotations that annotate other annotations, such as @Retention, @Target, @Documented, @Inherited, @Repeatable, @Native.

Custom annotations – user‑defined annotations that can also be meta‑annotated.

1.1 Built‑in Annotations

<code>class Parent {
    public void rewriteMethod() {}
}
class Child extends Parent {
    /**
     * Override parent method
     */
    @Override
    public void rewriteMethod() {}
    /**
     * Deprecated method
     */
    @Deprecated
    public void oldMethod() {}
    /**
     * Suppress warnings
     */
    @SuppressWarnings("keep run")
    public List infoList() {
        List list = new ArrayList();
        return list;
    }
}</code>

Standard annotations include:

@Override : indicates the method overrides a superclass method.

@Deprecated : marks the element as deprecated, causing a compiler warning.

@SuppressWarnings : disables specified compiler warnings.

1.2 Meta‑annotations

Four standard meta‑annotations were added in JDK 1.5: @Target, @Retention, @Documented, @Inherited. JDK 8 added @Repeatable and @Native.

@Target

Describes where an annotation can be applied (packages, types, class members, method parameters, local variables).
<code>public enum ElementType {
    TYPE, // class, interface, enum
    FIELD, // member variable
    METHOD, // member method
    PARAMETER, // method parameter
    CONSTRUCTOR, // constructor
    LOCAL_VARIABLE, // local variable
    ANNOTATION_TYPE, // annotation type
    PACKAGE, // package
    TYPE_PARAMETER, // type parameter (Java 8)
    TYPE_USE // any use of a type (Java 8)
}</code>

@Retention

Specifies how long annotations are retained: SOURCE, CLASS, or RUNTIME.
<code>public enum RetentionPolicy {
    SOURCE, // discarded by compiler
    CLASS, // stored in .class file (default)
    RUNTIME // retained at runtime, accessible via reflection
}</code>

Example of three retention policies:

<code>@Retention(RetentionPolicy.SOURCE)
public @interface SourcePolicy {}

@Retention(RetentionPolicy.CLASS)
public @interface ClassPolicy {}

@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimePolicy {}
</code>

When applied to methods, the compiler records annotations differently:

SourcePolicy annotations are not recorded in bytecode.

ClassPolicy annotations appear in RuntimeInvisibleAnnotations .

RuntimePolicy annotations appear in RuntimeVisibleAnnotations .

@Documented

Ensures the annotation is included in Javadoc.
<code>@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DocAnnotation {
    String value() default "default";
}
</code>

@Inherited

If a superclass is annotated with an @Inherited annotation, subclasses inherit it.
<code>@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface InheritedAnnotation {
    String[] values();
    int number();
}

@InheritedAnnotation(values={"brand"}, number=100)
public class UserInfo {}

public class Customer extends UserInfo {
    @Test
    public void testMethod() {
        Class clazz = Student.class;
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }
}
</code>

Running the above prints the inherited annotation on Customer .

@Repeatable (Java 8)

Allows multiple instances of the same annotation on a single element.
<code>@Repeatable(Pets.class)
public @interface Pet {
    String myPet();
}

public @interface Pets {
    Pet[] value();
}

@Pets({@Pet(myPet="dog"), @Pet(myPet="cat")})
public class RepeatAnnotationOV {
    public void workMethod() {}
}

@Pet(myPet="dog")
@Pet(myPet="cat")
public class RepeatAnnotationNV {
    public void workMethod() {}
}
</code>

@Native (Java 8)

Marks a field as usable by native code; rarely used.

1.3 Accessing Annotations via Reflection

The java.lang.reflect.AnnotatedElement interface provides methods to query annotations at runtime, but only if the annotation’s retention is RUNTIME.

isAnnotationPresent(Class&lt;? extends Annotation&gt;)

getAnnotation(Class&lt;? extends Annotation&gt;)

getAnnotations()

getAnnotationsByType(Class&lt;? extends Annotation&gt;)

getDeclaredAnnotation(Class&lt;? extends Annotation&gt;)

getDeclaredAnnotationsByType(Class&lt;? extends Annotation&gt;)

getDeclaredAnnotations()

1.4 Custom Annotations

After understanding built‑in and meta‑annotations, you can define your own.
<code>package com.helenlyn.common.annotation;

import java.lang.annotation.*;

/**
 * Fruit provider annotation
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
    int id() default -1;
    String name() default "";
    String address() default "";
}
</code>
<code>package com.helenlyn.common.dto;

import com.helenlyn.common.annotation.*;

public class AppleDto {
    @FruitName("Apple")
    private String appleName;

    @FruitColor(fruitColor=FruitColor.Color.RED)
    private String appleColor;

    @FruitProvider(id=1, name="helenlyn 贸易公司", address="福州xx路xxx大楼")
    private String appleProvider;
}
</code>

Utility class using reflection to read these annotations:

<code>public class FruitInfoUtil {
    public static String getFruitInfo(Class<?> clazz) {
        String name = "水果名称:";
        String color = "水果颜色:";
        String provider = "供应商信息:";
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(FruitName.class)) {
                FruitName fn = field.getAnnotation(FruitName.class);
                name += fn.value();
            } else if (field.isAnnotationPresent(FruitColor.class)) {
                FruitColor fc = field.getAnnotation(FruitColor.class);
                color += fc.fruitColor().toString();
            } else if (field.isAnnotationPresent(FruitProvider.class)) {
                FruitProvider fp = field.getAnnotation(FruitProvider.class);
                provider = "供应商编号:" + fp.id() + " 供应商名称:" + fp.name() + " 供应商地址:" + fp.address();
            }
        }
        return String.format("%s;%s;%s;", name, color, provider);
    }
}
</code>

Running the utility prints the fruit name, color, and provider details.

2 Understanding Annotation Principles

2.1 New Annotations in Java 8

@Repeatable

ElementType.TYPE_USE

ElementType.TYPE_PARAMETER

<code>// Example of TYPE_USE annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface MyNotNull {}

public class Example<T> {
    public @MyNotNull T test(@MyNotNull T a) {
        new ArrayList<@MyNotNull String>();
        return a;
    }
}
</code>

2.2 Do Annotations Support Inheritance?

Annotations themselves cannot extend another annotation, but a class can inherit annotations from its superclass if the annotation is marked with @Inherited.

3 Annotation Use Cases

Custom Annotations with AOP

Using a custom @DataSource annotation together with Spring AOP to switch data sources dynamically.
<code>@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String name() default "";
}
</code>
<code>@Aspect
@Component
public class DataSourceAspect implements Ordered {
    @Pointcut("@annotation(com.helenlyn.dataassist.annotation.DataSource)")
    public void dataSourcePointCut() {}

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource ds = method.getAnnotation(DataSource.class);
        String routeKey = ds.name();
        String current = DynamicDataSourceRouteHolder.getDataSourceRouteKey();
        if (StringUtils.isNotEmpty(current)) {
            routeKey = ds.name();
        }
        DynamicDataSourceRouteHolder.setDataSourceRouteKey(routeKey);
        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            DynamicDataSourceRouteHolder.clearDataSourceRouteKey();
        }
    }

    @Override
    public int getOrder() { return 1; }
}
</code>

Controller methods can now be annotated with @DataSource to route to different databases.

Data source routing example
Data source routing example
Result of routing
Result of routing
Multiple data source demo
Multiple data source demo
JavareflectionAnnotationsSpring AOPMeta‑annotationscustom annotations
Architecture & Thinking
Written by

Architecture & Thinking

🍭 Frontline tech director and chief architect at top-tier companies 🥝 Years of deep experience in internet, e‑commerce, social, and finance sectors 🌾 Committed to publishing high‑quality articles covering core technologies of leading internet firms, application architecture, and AI breakthroughs.

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.