@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface OperatorLog {String source() default "WEB"; //日志操作來源 默認是web,還有socket的String model() default ""; //操作模塊
}
這個代碼中的 @Target
和 @Retention
是元注解(meta-annotations),它們用于指定自定義注解的使用規則和行為。元注解本身并不直接影響注解的功能,而是控制注解如何被應用和如何被處理。
元注解解析:
-
@Target
:- 作用:指定自定義注解可以應用于哪些程序元素。常見的元素類型有:
ElementType.METHOD
:表示該注解只能應用于方法。ElementType.TYPE
:表示該注解可以應用于類、接口、枚舉等類型。ElementType.FIELD
:表示該注解可以應用于字段。ElementType.PARAMETER
:表示該注解可以應用于方法參數。ElementType.LOCAL_VARIABLE
:表示該注解可以應用于局部變量。ElementType.CONSTRUCTOR
:表示該注解可以應用于構造函數。ElementType.ANNOTATION_TYPE
:表示該注解可以應用于其他注解。ElementType.PACKAGE
:表示該注解可以應用于包。
在你的代碼中,
@Target(ElementType.METHOD)
表示OperatorLog
注解只能應用于方法上。也就是說,你不能將該注解應用于字段或類上。 - 作用:指定自定義注解可以應用于哪些程序元素。常見的元素類型有:
-
@Retention
:- 作用:指定注解在什么時機可用。常見的保留策略有:
RetentionPolicy.SOURCE
:注解僅存在于源代碼中,編譯后會被丟棄。RetentionPolicy.CLASS
:注解會被編譯器保留在.class
文件中,但在運行時不可用(默認行為)。RetentionPolicy.RUNTIME
:注解會被編譯并保留在.class
文件中,運行時可通過反射讀取。通常自定義注解都使用RUNTIME
保留策略,便于在運行時進行處理。
在你的代碼中,
@Retention(value = RetentionPolicy.RUNTIME)
表示OperatorLog
注解在運行時依然可用,可以通過反射來訪問。 - 作用:指定注解在什么時機可用。常見的保留策略有:
總結即為:
@Target
用來指定注解應用的場景,通常需要使用它來限制注解的應用范圍。@Retention
用來指定注解的生命周期,通常也需要使用它來確保注解在編譯后仍然可以被訪問(例如通過反射)。- 其他元注解:除了
@Target
和@Retention
,Java 還提供了其他元注解,如:@Documented
:表示該注解會出現在 Javadoc 中,適用于文檔化注解。@Inherited
:表示該注解可以被子類繼承(僅對類注解有效)。@Conditional(HAEnabledCheck.class)
這是核心的元注解,表示 @HaEnabled 注解的啟用依賴于條件判斷。@Conditional 注解接收一個類(這里是 HAEnabledCheck)作為參數,只有當這個類的判斷條件為 true 時,@HaEnabled 注解才會生效。
HAEnabledCheck 是一個條件類,通常是一個實現了 Condition 接口的類,它決定是否啟用帶有 @HaEnabled 注解的功能。
如果你希望在自定義注解上使用反射,或者限制注解應用的范圍,就需要使用這些元注解。
注解和反射
在Java中,“在注解上使用反射”指的是通過反射機制在運行時動態地讀取和處理注解的內容。反射是一種在程序運行時檢查和操作類、方法、字段等的能力,而注解是一種用于在代碼中添加元數據的機制。
反射和注解的關系
注解本身只是對代碼的標記,它不會自動執行某些操作,反而需要程序在運行時通過反射去讀取它,基于注解的信息做一些邏輯上的處理。例如,AOP(面向切面編程)就是通過反射讀取注解,來決定是否在方法執行前、后或者拋出異常時執行某些邏輯。
如何通過反射讀取注解
-
定義注解:
首先,你需要定義一個注解,并在注解上使用元注解(如@Target
,@Retention
)來指定注解的使用規則和生命周期。示例:
@Retention(RetentionPolicy.RUNTIME) // 保留到運行時 @Target(ElementType.METHOD) // 只允許用于方法 public @interface MyAnnotation {String value() default "defaultValue"; // 注解中的屬性 }
-
在代碼中使用注解:
然后,你可以在代碼中的方法上使用該注解,來標記某些方法。示例:
public class MyClass {@MyAnnotation(value = "Hello, World!")public void myMethod() {System.out.println("Executing myMethod...");} }
-
通過反射讀取注解:
接下來,通過反射,你可以獲取方法上的注解信息,并根據注解的內容來執行相應的操作。為了讀取注解,你可以使用Method
類的getAnnotation
方法。示例:
import java.lang.annotation.Annotation; import java.lang.reflect.Method;public class AnnotationExample {public static void main(String[] args) throws Exception {// 獲取 MyClass 類的字節碼對象Class<MyClass> clazz = MyClass.class;// 獲取 myMethod 方法的 Method 對象Method method = clazz.getMethod("myMethod");// 檢查該方法是否有 MyAnnotation 注解if (method.isAnnotationPresent(MyAnnotation.class)) {// 獲取 MyAnnotation 注解MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);// 讀取注解中的值System.out.println("Annotation value: " + annotation.value());}// 執行方法MyClass obj = new MyClass();obj.myMethod();} }
-
運行結果:
在運行時,程序會讀取myMethod
上的MyAnnotation
注解,并輸出注解中的value
屬性內容,執行結果如下:Annotation value: Hello, World! Executing myMethod...
為什么需要在注解上使用反射?
反射與注解的結合非常強大,它能夠讓你在程序運行時根據注解的內容動態地改變程序行為,這就是所謂的“注解驅動編程”。
常見的應用場景有:
-
AOP(面向切面編程):
使用注解標記方法,然后通過反射獲取注解的值,執行一些邏輯(如日志、權限檢查、事務控制等)對方法進行增強。比如Spring中的@Transactional注解,使用反射來判斷方法是否需要事務支持。@Transactional public void someMethod() {// 業務邏輯 }
-
依賴注入(DI):
在Spring等框架中,通過注解(如@Autowired
或@Inject
)標記字段或方法,框架通過反射讀取這些注解并自動注入依賴。@Autowired private MyService myService;
-
ORM(對象關系映射)框架:
在ORM框架中,注解可以標記實體類與數據庫表的映射關系,框架通過反射讀取注解,在數據庫中進行查詢、插入等操作。@Entity public class User {@Idprivate Long id;private String name; }
-
自定義注解的處理:
可以創建自定義注解,并結合反射來控制程序的行為。例如,創建一個注解用于記錄日志,或者控制某個方法的訪問權限等。
小結:
“在注解上使用反射”就是通過反射機制,在程序運行時,動態地獲取注解的值,并基于這些值來執行一些操作。反射提供了靈活的動態行為,注解則提供了豐富的元數據支持,兩者結合能夠實現很多靈活的功能,例如日志記錄、事務管理、權限控制等。