Java Annotation 注釈
注记の概要
アノテーションはJava 5で導入されたメタデータメカニズムです。クラス、メソッド、フィールド、コンストラクタ、パラメータなどのプログラム要素に説明や設定情報を追加できます。
アノテーション自体は通常、ビジネスロジックを直接実行するのではなく、コンパイラ、JVM、リフレクションコード、フレームワークによって読み込まれ処理されます。一般的な注記には、@Override、@Deprecated、@SuppressWarningsがある。
注釈の性質
カスタム注記では、@interface宣言が使用されます。コンパイルされると、アノテーション型は本質的にjava.lang.annotation.Annotationインタフェースを継承した特別なインタフェースになります。
public @interface MyAnnotation {
String value() default "";
}
注釈内のメンバー宣言は、パラメータなしメソッドに似ています。メンバ型は、基本型、String、Class、列挙、その他の注釈、およびこれらの型の1 次元配列のみを使用できます。
メタアノテーション
メタ注記は、注記のタイプを修飾する注記です。
@Retention
@Retentionは、注記の保持フェーズを指定するために使用されます。
| 値 | 説明 |
|---|---|
RetentionPolicy.SOURCE | ソースコードにのみ存在し、コンパイル後に破棄される |
RetentionPolicy.CLASS | バイトコードファイルを書き込むが、通常は実行時にリフレクションでは読み取れない |
RetentionPolicy.RUNTIME | 実行時に予約され、リフレクションで読み取ることができる |
プログラム実行中に解析する必要がある注釈は、RetentionPolicy.RUNTIMEを使用する必要があります。
@Target
@Targetは、注記が修飾できる位置を制限するために使用される。
| 値 | 適用場所 |
|---|---|
ElementType.TYPE | クラス、インタフェース、列挙型、またはコメント型 |
ElementType.METHOD | 方法論は |
ElementType.FIELD | フィールドフィールド |
ElementType.PARAMETER | メソッドパラメータ |
ElementType.CONSTRUCTOR | コンストラクター。 |
ElementType. ANNOTATION_TYPE | 注記の種類 |
その他の一般的なメタ注釈
@DocumentedAPIドキュメントの生成時に注釈を含めます。@Inherited:クラスレベルのアノテーションをサブクラスに継承できますが、メソッドやフィールドには影響しません。@Repeatable:同じ場所で同じ種類のアノテーションを繰り返し使用できます。
カスタムコールアウトの定義と使用
注记の
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 {};
}注记の使用
@MyAnnotation(
value = "用户类",
age = 20,
tags = {"测试", "业务"}
)
public class User {
@MyAnnotation("打印方法")
public void print() {
System.out.println("执行 print() 方法");
}
}valueという名前のメンバーが1つだけ割り当てられる場合、メンバー名を省略できます。
@MyAnnotation("用户类")
public class User {
}
リフレクションによるコールアウトの解析
ランタイムコールアウトはリフレクションによって読み取ることができます。一般的な方法としては、isAnnotationPresent()、getAnnotation()、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());
}
}
}注釈はメタデータのみを保持します。実際の機能はコンパイラ、リフレクション、フレームワークによって実装される必要があります。
カスタム権限検証
アクセス権注記の定義
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();
}ビジネスメソッドでのコールアウトの使用
public class OrderService {
@RequirePermission("order:delete")
public void deleteOrder(Long id) {
System.out.println("删除订单:" + id);
}
}
コールアウトの解析とメソッドの実行
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);
}
}この例では、アノテーション+リフレクションの基本的な考え方を示しています。実際のプロジェクトでのユーザID、ロール、パーミッションセット、例外処理は、通常、セキュリティフレームワークによって管理されます。
コールアウトを使用したデータベーステーブルのマッピング
全体的な考え方
クラス·レベルの注釈を使用してテーブル名を記録し、フィールド·レベルの注釈を使用してカラム名を記録し、リフレクションを使用してSQLを生成し、オブジェクト·マッピングを完了できます。
テーブル名の注釈
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();
}フィールドの注記
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;
}エンティティークラスバインド注釈
@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 接続ツール
接続オブジェクトは、グローバルシングルトンとして長期再利用しないでください。各データベース操作は接続を取得し、操作終了後にリソースをシャットダウンするため、同時使用や異常復旧が容易になります。
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);
}
}注釈解析と一般的なJDBCツール
新規オブジェクトの追加
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);
}
}
}プライマリ·キーに基づくオブジェクトのクエリー
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);
}
}
}テストコールのテスト
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);
}
}使用上の注意事項
- アノテーションは実際の機能を生成するためにパーサと連携する必要がある。
- 実行時にリフレクションによって読み取られる注記は、
RetentionPolicy.RUNTIMEを使用する必要があります。 - テーブル名とカラム名は、
PreparedStatementのプレースホルダを介して渡すことはできません。したがって、マッピングされた値は信頼できる注釈から取得する必要があり、ユーザー入力を直接使用することはできません。 - 汎用マッピングツールは、型変換、null 値、継承フィールド、自己インクリメントプライマリキー、トランザクションなどの問題も扱う必要があります。
- 本番プロジェクトは通常、成熟した持続性層フレームワークを使用し、ORM 機能の完全な再実装は推奨されません。
気に入ったならばコメントを残してくださいね~