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.
| value | description |
|---|---|
RetentionPolicy.SOURCE | only exists in the source code, |
RetentionPolicy.CLASS | writes bytecode files, but |
RetentionPolicy.RUNTIME | is 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.
| value | application location |
|---|---|
ElementType.TYPE | Class, interface, enumeration or annotation type |
ElementType.METHOD | Method |
ElementType.FIELD | Field |
ElementType.PARAMETER | Method Parameters |
ElementType.CONSTRUCTOR | Builder |
ElementType.ANNOTATION_TYPE | Comment 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
PreparedStatementplaceholders, 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~