annotation

Published 2026-07-29 10:56 Updated 2026-07-29 10:56 1537 words 8 min read ... Page views

This article introduces the basic concepts of Java annotations, the definition and use of meta annotations, and custom annotations, and the practical application of annotations in permission verification and database mapping. Through the reflection mechanism, annotations can be parsed and used to implement business logic, but their essence is metadata, and the actual function needs to be implemented by a framework or program. The article emphasizes that annotations need to be combined with run-time retention policy (RUNTIME) and security verification, and that duplicate implementation of ORM functions should be avoided in actual projects. It is recommended to use mature frameworks.

Java Annotation (annotation)

Overview of annotations

Annotations are a metadata mechanism introduced in Java 5. It can add descriptions or configuration information to program elements such as classes, methods, fields, constructors, parameters, etc.

The annotations themselves usually do not directly execute business logic, but are read and processed by a compiler, JVM, reflection code, or framework. Common annotations include @Override, @Deprecated and @SuppressWarnings.

The nature of annotations

Custom annotations are declared using @interface. After compilation, the annotation type is essentially a special interface that inherits the java.lang.annotation.Annotation interface.

public @interface MyAnnotation {
    String value() default "";
}

Member declarations in annotations are similar to parameterless methods. Member types can only use basic types, String, Class, enumerations, other annotations, and one-dimensional arrays of these types.

meta annotation

Meta-annotations are annotations used to modify the type of annotation.

@Retention

@Retention is used to specify the retention phase of annotations.

valuedescription
RetentionPolicy.SOURCEonly exists in the source code,
RetentionPolicy.CLASSwrites bytecode files, but
RetentionPolicy.RUNTIMEis retained during operation,

Notes that need to be parsed during program execution must use RetentionPolicy.RUNTIME.

@Target

@Target is used to limit where annotations can be modified.

valueapplication location
ElementType.TYPEClass, interface, enumeration or annotation type
ElementType.METHODMethod
ElementType.FIELDField
ElementType.PARAMETERMethod Parameters
ElementType.CONSTRUCTORBuilder
ElementType.ANNOTATION_TYPEComment Type

Other commonly used meta annotations

  • @Documented: Include this annotation when generating API documents.
  • @Inherited: Allowing class-level annotations to be inherited by subclasses, but not on methods and fields.
  • @Repeatable: Allow the same annotation to be reused in the same location.

Define and use custom annotations

definition annotation

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAnnotation {
    String value() default "";

    int age() default 18;

    String[] tags() default {};
}

using annotations

@MyAnnotation(
        value = "用户类",
        age = 20,
        tags = {"测试", "业务"}
)
public class User {
    @MyAnnotation("打印方法")
    public void print() {
        System.out.println("执行 print() 方法");
    }
}

When the annotation has only one member named value that needs to be assigned, the member name can be omitted.

@MyAnnotation("用户类")
public class User {
}

Analyze annotations through reflection

Run-time annotations can be read through reflection. Common methods include isAnnotationPresent(), getAnnotation() and getDeclaredAnnotations().

import java.lang.reflect.Method;
import java.util.Arrays;

public class AnnotationTest {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<User> userClass = User.class;

        if (userClass.isAnnotationPresent(MyAnnotation.class)) {
            MyAnnotation classAnnotation = userClass.getAnnotation(MyAnnotation.class);

            System.out.println("value:" + classAnnotation.value());
            System.out.println("age:" + classAnnotation.age());
            System.out.println("tags:" + Arrays.toString(classAnnotation.tags()));
        }

        Method printMethod = userClass.getDeclaredMethod("print");
        MyAnnotation methodAnnotation = printMethod.getAnnotation(MyAnnotation.class);

        if (methodAnnotation != null) {
            System.out.println("方法注解:" + methodAnnotation.value());
        }
    }
}

Annotations are only responsible for storing metadata. The real functionality must be implemented by a compiler, reflector, or framework.

Custom permission verification

Define permission annotations

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequirePermission {
    String value();
}

Using annotations on business methods

public class OrderService {
    @RequirePermission("order:delete")
    public void deleteOrder(Long id) {
        System.out.println("删除订单:" + id);
    }
}

Parse annotations and execute methods

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class PermissionInterceptor {
    private static final String CURRENT_PERMISSION = "order:delete";

    public static void invokeMethod(
            Object target,
            Method method,
            Object... arguments
    ) throws InvocationTargetException, IllegalAccessException {
        RequirePermission annotation = method.getAnnotation(RequirePermission.class);

        if (annotation != null) {
            String requiredPermission = annotation.value();

            if (!requiredPermission.equals(CURRENT_PERMISSION)) {
                throw new SecurityException(
                        "权限不足,需要权限:" + requiredPermission
                );
            }
        }

        method.invoke(target, arguments);
    }

    public static void main(String[] args) throws ReflectiveOperationException {
        OrderService orderService = new OrderService();
        Method method = OrderService.class.getMethod("deleteOrder", Long.class);

        invokeMethod(orderService, method, 10L);
    }
}

This example only demonstrates the basic idea of “annotation plus reflection”. User identities, roles, permission sets, and exception handling in real projects are usually managed uniformly by the security framework.

Mapping database tables using annotations

overall idea

You can use class-level annotations to record table names, field-level annotations to record column names, and then generate SQL through reflection and complete object mapping.

table name annotation

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String value();
}

field annotation

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    String value();

    boolean primaryKey() default false;
}

Entity class binding annotation

@Table("book")
public class MyBook {
    @Column(value = "id", primaryKey = true)
    private Integer bookId;

    @Column("isbn")
    private Integer isbn;

    @Column("name")
    private String name;

    @Column("price")
    private Integer price;

    public MyBook() {
    }

    public MyBook(Integer bookId, Integer isbn, String name, Integer price) {
        this.bookId = bookId;
        this.isbn = isbn;
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "MyBook{" +
                "bookId=" + bookId +
                ", isbn=" + isbn +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

JDBC connection tool

Connected objects should not be reused for long periods of time as global singletons. Each database operation obtains a connection and shuts down resources after the operation ends, which is more conducive to concurrent use and abnormal recovery.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public final class DBUtil {
    private static final String URL =
            "jdbc:mysql://localhost:3306/java2506?serverTimezone=UTC&characterEncoding=utf8";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "1234";

    private DBUtil() {
    }

    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USERNAME, PASSWORD);
    }
}

Annotations parsing and common JDBC tools

new object

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class AnnotationJdbcUtil {
    public static int insert(Object entity) {
        Class<?> entityClass = entity.getClass();
        Table table = entityClass.getAnnotation(Table.class);

        if (table == null) {
            throw new IllegalArgumentException("实体类缺少 @Table 注解");
        }

        List<String> columns = new ArrayList<>();
        List<Object> values = new ArrayList<>();

        for (Field field : entityClass.getDeclaredFields()) {
            Column column = field.getAnnotation(Column.class);

            if (column == null) {
                continue;
            }

            field.setAccessible(true);
            columns.add(column.value());

            try {
                values.add(field.get(entity));
            } catch (IllegalAccessException e) {
                throw new RuntimeException("读取字段失败:" + field.getName(), e);
            }
        }

        String placeholders = String.join(",", java.util.Collections.nCopies(
                columns.size(),
                "?"
        ));

        String sql = "insert into " + table.value() +
                "(" + String.join(",", columns) + ") values(" +
                placeholders + ")";

        try (Connection connection = DBUtil.getConnection();
             PreparedStatement statement = connection.prepareStatement(sql)) {

            for (int index = 0; index < values.size(); index++) {
                statement.setObject(index + 1, values.get(index));
            }

            return statement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException("执行新增操作失败", e);
        }
    }
}

Query objects based on primary key

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class AnnotationJdbcQueryUtil {
    public static <T> T getById(Class<T> entityClass, Object id) {
        Table table = entityClass.getAnnotation(Table.class);

        if (table == null) {
            throw new IllegalArgumentException("实体类缺少 @Table 注解");
        }

        List<Field> mappedFields = new ArrayList<>();
        List<String> columnNames = new ArrayList<>();
        String primaryKeyColumn = null;

        for (Field field : entityClass.getDeclaredFields()) {
            Column column = field.getAnnotation(Column.class);

            if (column == null) {
                continue;
            }

            mappedFields.add(field);
            columnNames.add(column.value());

            if (column.primaryKey()) {
                primaryKeyColumn = column.value();
            }
        }

        if (primaryKeyColumn == null) {
            throw new IllegalArgumentException("实体类未标记主键字段");
        }

        String sql = "select " + String.join(",", columnNames) +
                " from " + table.value() +
                " where " + primaryKeyColumn + " = ?";

        try (Connection connection = DBUtil.getConnection();
             PreparedStatement statement = connection.prepareStatement(sql)) {

            statement.setObject(1, id);

            try (ResultSet resultSet = statement.executeQuery()) {
                if (!resultSet.next()) {
                    return null;
                }

                T entity = entityClass.getDeclaredConstructor().newInstance();

                for (Field field : mappedFields) {
                    Column column = field.getAnnotation(Column.class);
                    field.setAccessible(true);
                    field.set(entity, resultSet.getObject(column.value()));
                }

                return entity;
            }
        } catch (SQLException | ReflectiveOperationException e) {
            throw new RuntimeException("查询对象失败", e);
        }
    }
}

test call

public class JdbcAnnotationTest {
    public static void main(String[] args) {
        MyBook book = new MyBook(
                6,
                1006,
                "Java 反射与注解程序设计",
                66
        );

        int affectedRows = AnnotationJdbcUtil.insert(book);
        System.out.println("受影响行数:" + affectedRows);

        MyBook result = AnnotationJdbcQueryUtil.getById(MyBook.class, 6);
        System.out.println("查询结果:" + result);
    }
}

precautions for use

  • Annotations need to cooperate with the parser to produce actual functionality.
  • Notes read through reflection at runtime must use RetentionPolicy.RUNTIME.
  • Table names and column names cannot be passed in through PreparedStatement placeholders, so the mapping values must come from trusted annotations and cannot be directly entered by user.
  • Universal mapping tools also need to deal with issues such as type conversions, null values, inherited fields, self-incremental primary keys, and transactions.
  • Production projects usually use mature persistence layer frameworks, and it is not recommended to repeatedly implement full ORM functions.

If you enjoyed this, leave a comment~

... Page views
© 2026 跨越星轨的客 @Hoshiumi
Powered by theme astro-koharu · Inspired by Shoka