一、注解簡介
注解也叫元數據,是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 }