一、註解簡介
註解也叫元數據,是JDK1.5版本開始引入的一個特性,用於對代碼進行標記說明,可以對包、類、接口、字段、方法參數、局部變量等進行註解修飾
1.1、註解的類型
1、JDK註解和框架註解:JDK本身提供了很多註解比如@Resource、@PostConstruct等;另外常用的框架也提供了很多註解,比如Spring的@Autowired,@Service等等,這些註解使用時會自動被JDK或框架進行識別解析;
2、元註解:元註解用於修飾註解的,如@Retention(標明註解被保留的階段)、@Target(標明註解使用的範圍)、@Inherited(標明註解可繼承)、@Documented(標明是否生成javadoc文檔)
3、自定義註解:用戶可以根據自行需求自定義註解
1.2、元註解
@Retention:定義註解的生命週期,默認是CLASS,取值範圍如下:
SOURCE | 在編譯階段被拋棄,通常用於編譯時使用,比如@Override註解 |
CLASS | 在編譯階段會被寫入字節碼,當類加載的時候會被丟棄 |
RUNTIME | 不會被丟棄,運行期間也可以使用,所以通過反射機制就可以讀取該註解的信息,通常自定義註解都會採用RUNTIME類型 |
@Target:定義註解可以修飾的目標,默認是可以修飾任意目標,取值範圍如下
TYPE | 用於描述類、接口或enum聲明,如@Service、@Component等 |
FIELD | 用於描述屬性,比如@JSONField等 |
METHOD | 用於描述方法,比如@Override |
PARAMETER | 用於描述方法參數,比如Mybatis框架中的@Param |
CONSTRUCTOR | 用於描述構造函數 |
LOCAL_VARIABLE | 用於描述局部變量 |
ANNOTATION_TYPE | 用於描述註解類型,比如@Target本身,@Retention,@Document註解等 |
PACKAGE | 用於描述包名 |
TYPE_PARAMETER | 用於描述參數類型 |
TYPE_USE | 表示該註解能使用在使用類型的任意語句中 |
@Inherited:定義註解是否繼承給子類
當@Inherited註解修飾了一個註解,那麼如果這個註解修飾了一個類,那麼這個類的子類也會繼承該註解
@Documented:定義註解是否將註解信息加到Java文檔中
1.3、註解的組成
註解通常有幾個部分組成,包括修飾該註解的元註解,註解名稱和註解方法,當然也可以將註解僅當作標記作用,沒有任何方法也行,比如@Override註解就沒有任何方法,僅當作標記使用
二、註解的使用
通常我們Web服務提供接口需要用戶登錄之後纔可以訪問,此時如果每個接口都判斷下用戶是否登錄就會冗餘很多的代碼,所以需要在執行接口方法之前有一層驗證用戶登錄的邏輯,可以通過過濾器,攔截器等方式實現,此時就可以配合註解來實現,在需要進行登錄驗證的方法上添加一個自定義的註解,然後每個添加了註解的方法就需要驗證登錄,沒有註解的方法就不需要登錄,實現方式如下:
自定義註解@Logined
@Documented @Target(ElementType.METHOD) /** 修飾方法*/ @Retention(RetentionPolicy.RUNTIME)/** 生命週期爲運行期間*/ public @interface Logined { /** 定義方法,如果沒有登錄的情況下是否直接拋異常*/ boolean exception() default false; }
定義了註解之後就可以直接使用,但是想要使註解的效果生效,就需要有一套獲取註解並處理業務的邏輯,此時就離不開Java的反射機制,需要通過反射機制獲取到修飾在方法、類、屬性上的註解來進行判斷是否加了註解。
另外在使用Spring框架時,可以配置AOP來配合使用自定義註解,比如以下案例就是用來處理@Logined註解的邏輯:
@Aspect @Component public class LoginAspect { @Around("@annotation(com.test.annotation.Logined)") public Object doBefore(ProceedingJoinPoint jp) throws Throwable { if (MessageConfig.LOCAL.get() == null) { System.out.println("請求用戶爲空,返回401:" + jp.getSignature().getName()); return Result.returnUnauthorized(); } return jp.proceed(); } }
三、註解的實現原理
註解本身沒有任何邏輯,只能起到標記的作用,實現的邏輯完全取決於處理註解的邏輯,而處理註解就需要先找到註解,此時就離不開Java的反射機制,主要是通過Constructor、Class、Method、Field等反射相關類的getAnnotation(Class annotationClass)方法獲取對應的註解,如果能獲取到註解那麼就表示被註解修飾了,案例如下:
1 /** 1.查找類上的註解 */ 2 Annotation classAnnotation = cla.getAnnotation(Logined.class); 3 if(classAnnotation != null){ 4 System.out.println("類被@Logined註解修飾"); 5 } 6 7 /** 2.查找方法上的註解 */ 8 Method[] methods = cla.getMethods(); 9 for (Method method : methods){ 10 if(method.getAnnotation(Logined.class) != null){ 11 System.out.println("方法:" + method.getName() + "被註解@Logined" + "修飾"); 12 } 13 } 14 15 /** 3.查找屬性上的註解 */ 16 Field[] fields = cla.getFields(); 17 for (Field field : fields){ 18 if(field.getAnnotation(Logined.class) != null){ 19 System.out.println("屬性:" + field + "被註解@Logined" + "修飾"); 20 } 21 } 22 23 /** 4.查找構造函數上的註解 */ 24 Constructor constructor = cla.getConstructor(String.class); 25 if(constructor.getAnnotation(Logined.class)!=null){ 26 System.out.println("構造器被@Logined註解修飾"); 27 }