Java 註解自 JDK 5.0 (2004) 引入以來,已成為現代 Java 開發的基石。Spring、Hibernate、JUnit、MyBatis 等主流框架大量依賴註解實現聲明式配置、依賴注入、事務管理、測試標記等功能。本文系統梳理註解核心概念、內建註解、元註解、自訂註解定義與實務應用模式。
java.lang.annotation.Annotation 的介面,編譯期/運行期可透過反射讀取// 定義
public @interface ThreadSafe {}
// 使用
@ThreadSafe
public class ImmutablePoint {
private final int x, y;
// ...
}
// 定義 (元素名稱必須為 value)
public @interface Priority {
int value() default 0;
}
// 使用 - 可省略元素名稱
@Priority(10)
public class CriticalTask {}
// 等同於 @Priority(value = 10)
// 定義
public @interface AuditLog {
String operation();
String module() default "SYSTEM";
boolean async() default true;
int[] relatedIds() default {};
}
// 使用 - 必須指定所有無預設值的元素
@AuditLog(
operation = "DELETE_USER",
module = "USER_MGMT",
async = false,
relatedIds = {1001, 1002}
)
public void deleteUser(Long userId) { ... }
| 元註解 | 用途 | 關鍵屬性 |
|---|---|---|
@Target | 限定註解可套用的程式元素類型 | ElementType.TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE |
@Retention | 指定註解保留階段 | RetentionPolicy.SOURCE, CLASS, RUNTIME |
@Documented | 標記此註解應包含在 Javadoc 中 | 無 |
@Inherited | 允許子類別繼承父類別的此註解 (僅限 TYPE 目標) | 無 |
@Repeatable | 允許同一元素重複套用同一註解 (Java 8+) | value = 容器註解類別 |
@Native | 標示常數可能被原生程式碼引用 (Java 8+) | 無 |
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ApiEndpoint {
String path();
RequestMethod method() default RequestMethod.GET;
String description() default "";
int version() default 1;
boolean deprecated() default false;
String[] tags() default {};
}
public enum RequestMethod { GET, POST, PUT, DELETE, PATCH }
// 使用範例
@ApiEndpoint(
path = "/users/{id}",
method = RequestMethod.GET,
description = "取得使用者資料",
version = 2,
tags = {"user", "query"}
)
public class UserController {
@ApiEndpoint(path = "/users", method = RequestMethod.POST, description = "建立使用者")
public Response createUser(Request request) { ... }
}
public class AnnotationProcessor {
public static void scanEndpoints(Class> clazz) {
// 類別層級註解
if (clazz.isAnnotationPresent(ApiEndpoint.class)) {
ApiEndpoint ep = clazz.getAnnotation(ApiEndpoint.class);
System.out.println("Controller: " + ep.path() + " v" + ep.version());
}
// 方法層級註解
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(ApiEndpoint.class)) {
ApiEndpoint ep = method.getAnnotation(ApiEndpoint.class);
System.out.printf(" %s %s - %s%n",
ep.method(), ep.path(), ep.description());
}
}
}
}
實作 javax.annotation.processing.Processor 介面,可在編譯期掃描註解並生成程式碼、資源檔、配置檔等。常見應用:
@SupportedAnnotationTypes("com.example.ApiEndpoint")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class ApiEndpointProcessor extends AbstractProcessor {
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment env) {
for (Element element : env.getElementsAnnotatedWith(ApiEndpoint.class)) {
// 生成 OpenAPI/Swagger 文檔、路由註冊碼、測試樁等
generateRouterCode(element);
}
return true;
}
}
@NonNull String[] arr、List<@NonNull String>、(@NonNull String) -> ... (需 @Target(TYPE_USE))@Repeatable(Schedules.class) 允許 @Schedule(...) 寫多次-parameters 讓反射可讀取方法參數名