啊啊,终于搞明白了,原来注解是这么一回事。6000+字理解注解【一】
文章目錄
- 前言
- 復習注解
- 元注解
- @Retention 存活時間
- @Documented 文檔
- @Target 目標
- @Inherited 繼承
- @Repeatable 可重復
- 注解屬性
- 注解實現-反射
- 反射獲取注解方法
- 注解的使用場景
- 后續
- 導航
前言
這兩天在gitHub上看一個開源項目,發現項目中有許多自定義注解。雖然在學JavaSE的時候學個注解,但是主要講的是如何自定義注解,和一些元注解的知識點。并沒有涉及到注解如何實現具體的功能。直到看到這個項目,突然醍醐灌頂。
由于篇幅過長,分成兩篇來寫。
復習注解
注解是在Java 1.5 的時候被引入的,注解的創建與接口十分相似。就是在interface關鍵字前面加一個@符號。
public @interface MyAnnotation{ }創建玩注解之后就可以在想要添加主機的地方使用了,但是想要讓注解能夠正常工作,還需要給它化化妝。什么是化妝那,這里就要引入元注解的一個概念了,元注解他也是一個注解,只不過它是用來修飾注解的注解。有點迷?不慌咱慢慢看。
元注解
元注解一共有五種分別為:
- @Retention
- @Documented
- @Target
- @Inherited
- @Repeatable
他們的使用方法就是在創建注解的時候在,所要創建的注解上使用他們:
@Retention 存活時間
Retention中文意思是保留的意思。當@Retention應用到一個注解上的時候 ,通過參數可以設置這個注解的存活時間。什么是存活時間,就是說通過不同參數可以確定@Retention修飾的注解在那種情況下會消失,是保留到源碼階段、還是編譯階段、還是加載到JVM那。
下面看一下它的參數:
- RetentionPolicy.SOURCE
注解只被保留到編譯階段,當編譯器編譯到它的時候看到參數為 RetentionPolicy.SOURCE會直接將其從源碼中剔除。
- RetentionPolicy.SOURCE
注解制備保留到編譯進行的時候,也就是能保證在編譯期間注解也存在,但是不能進入JVM中。
- RetentionPolicy.SOURCE
注解可以保留到程序中,它會被加載到JVM中從而在服務中起作用。
@Documented 文檔
這個注解,翻譯過來就是文檔。那他并沒有其他功能上的作用,主要作用就是能講注解中的元素包含到Javadoc中去。
@Target 目標
Target 有目標的意思,他的意思就是說,這個注解能夠使用的地方是哪里,是方法上、類上還是參數上。我們通過它的參數就可以進行設置。
| ElementType.ANNOTATION_TYPE | 可以使用在注解上 |
| ElementType.CONSTRUCTOR | 可以在構造方法上使用 |
| ElementType.FIELD | 可以在屬性上使用注解 |
| ElementType.LOCAL_VARIABLE | 作用在局部變量上 |
| ElementType.METHOD | 作用在方法上 |
| ElementType.PACKAGE | 作用在包上 |
| ElementType.PARAMETER | 作用在方法內的參數上 |
| ElementType.TYPE | 可以給類型進行注解,比如類、接口、枚舉 |
@Inherited 繼承
Inherited 意思為繼承,這個元注解的作用有點繞。就是說如果@Inherited注解作用與自定義注解@MyAnnotation上,然后在A類上使用了自定義注解,然后A類的子類,就相當于也擁有@MyAnnotation注解。
/*自定義注解*/ @Inherited public @interface MyAnnotation{ } --------------------------- /*A類*/ @MyAnnotation public class A{ }/*B類繼承A類*/ public class B extent A{ }/* //此時,B類也繼承了A類的注解。相當于 @MyAnnotation public class B extent A{ } */@Repeatable 可重復
這個注解是在Java 1.8的時候加進來的。那可重復是什么意思那,就是說使用注解的時候可以同時使用多次注解。
public class A{ @MyAnnotation("教師") @MyAnnotation("公務員")public void test(){}}如果想實現這樣的效果,那么就需要使用@Repeatable注解修飾@MyAnnotation注解。@MyAnnotation才可以在一個方法或其它地方使用多次。
注解屬性
注解的屬性其實就好像實體類里面的屬性,只是寫法上有稍微的不同。但是注解是沒有方法的,
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) ... ... public @interface MyAnnotation{ int id(); String value(); }注意這里id()和value()表示的不是方法而是屬性也可叫做注解的變量。
注解的賦值方法就是在使用注解的時候,通過注解中的對應屬性="xxx"的形式
注解屬性可以設置默認值,也就是如果使用注解時沒有賦予特定的值,就使用默認值。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) ... ... public @interface MyAnnotation{ int id() default 101; String value(); }這里還有一個注意點,如果注解中只有一個名字為value的屬性的時候,應用這個注解的時候可以直接填寫屬性值。
pulic @interface MyAnnotation{String value; } @MyAnnotation("Hi") public void test(){ }如果定義的這個注解中沒有屬性值,我們在使用注解的時候就不用在寫括號了
@MyAnnotation public void test(){ }注解實現-反射
注解的知識經過上面的內容,應該能夠有個了解。那么我們定義好后注解,也是用了,但是并沒有什么實質的效果。哪有人該說了那定義有什么用?其實不然,如果真的沒有用那么Java官方就不會定義注解了。
好下面我們就給自定義的注解注入靈魂,實現當使用注解后完成相應的功能。那該如何實現那,注解中又不能寫方法,之有屬性值, 我們也不能通過new獲取注解。這里就要提到一個比較重要的知識點就是反射。
我們可以通過反射獲取作用在類或者方法上的注解讓,后進行對應的處理。
反射獲取注解方法
注解通過反射獲取。首先通過class對象的isAnnotationPresent()方法判斷他是否應用了某注解。
public Boolean isAnnotationPresent(Class<? extent Annotation> annotationClass) {}然后通過getAnnotation()方法來獲取Annotation對象。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}或者是getAnnotations()方法
public Annotation[] getAnnotations(){}前一種方法返回指定類型的注解,后一種方法返回注解到這個元素上的所有注解。
如果獲取到的Annotation如果不為null,則就可以調用他們的屬性方法了。比如
運行結果:
id:101上面是獲取類上的注解,其實方法、屬性的注解都是可以通過反射進行獲取的。
自定注解:
編寫測試類
public class TestAnnotation {@MyAnnotation("Hi")public void testMethod(){System.out.println("fun");}public static void main(String[] args) throws NoSuchMethodException {Class<TestAnnotation> aClass = TestAnnotation.class;Method msg = aClass.getDeclaredMethod("testMethod");msg.setAccessible(true);MyAnnotation annotation = msg.getAnnotation(MyAnnotation.class);System.out.println(annotation.value());} }運行結果
Hi這里需要注意一下,如果想要注解在運行時能夠獲取到,那么必須加上@Retention(RetentionPolicy.RUNTIME)這個注解參數,因為這個參數是指,注解能在jvm中被執行。
注解的使用場景
到這里應該大家對注解都有一定的了解了,但是還是可能會有疑惑注解到底有什么用吶。
我們不妨先看看官方的回答:
注解是一系列元數據,它提供數據用來解釋程序代碼,但是注解并非是所解釋的代碼本身的一部分。注解對于代碼的運行效果沒有直接影響。
注解有許多用處,主要如下:
- 提供信息給編譯器: 編譯器可以利用注解來探測錯誤和警告信息
- 編譯階段時的處理: 軟件工具可以用來利用注解信息來生成代碼、Html文檔或者做其它相應處理。
- 運行時的處理: 某些注解可以在程序運行的時候接受代碼的提取
值得注意的是,注解不是代碼本身的一部分。
從官方的話我們可以看出注解的作用并不是來寫主要業務的,而是通過注解實現一些對代碼的處理。
如果大家用過LomBok在實體類上面加一個@Data就可以幫助我們生成實體類的get和set的方法,在或者說Swagger生成接口文檔,在對應的接口上加上對應的注解就可以實現生成對應的接口文檔。注解只是起到到了標簽的作用,具體的實現方式是有對應的程序獲取到注解這個標識,然后去處理產生的。
那么我們可以用注解做什么呢?
現在我們就動手寫一個自己的注解,通過這個注解來檢測程序是否報錯。這是一個簡單的小案例。
首先寫一個注解@Jiance
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Jiance {}要測試的程序Cheshi并在方法上加上注解
public class Cheshi {@Jiancepublic void suanShu(){System.out.println("1234567890");}@Jiancepublic void jiafa(){System.out.println("1+1="+1+1);}@Jiancepublic void jiefa(){System.out.println("1-1="+(1-1));}@Jiancepublic void chengfa(){System.out.println("3 x 5="+ 3*5);}@Jiancepublic void chufa(){System.out.println("6 / 0="+ 6 / 0);}public void ziwojieshao(){System.out.println("我寫的程序沒有 bug!");} }運行檢測程序TestJiance
public class TestJiance {public static void main(String[] args) {Cheshi cheshi = new Cheshi();Class<Cheshi> cheshiClass = Cheshi.class;Method[] declaredMethods = cheshiClass.getDeclaredMethods();StringBuilder log = new StringBuilder();log.append("**************日志****************\n");int num = 0;for (Method declaredMethod : declaredMethods) {Jiance annotation = declaredMethod.getAnnotation(Jiance.class);if (annotation!=null){try {declaredMethod.setAccessible(true);declaredMethod.invoke(cheshi,null);} catch (Exception e) {num++;log.append(declaredMethod.getName()+":error:"+e.getCause().getMessage()+"\n");}}}log.append("cheshi has "+num+ " error");System.out.println(log);} }執行結果:
1+1=11 1234567890 1-1=0 3 x 5=15 **************日志**************** chufa:error:/ by zero cheshi has 1 error這樣我們就成了這小的案例。我們通過我們自定義的注解,來檢測所有cheshi類中有錯的方法。
注解的作用主要取決于你想用它做什么。
后續
利用反射獲取注解并實現功能通過上面已經實現了,但是還有一種方式也可以獲取注解,并實現對應的功能邏輯。
那就是利用Spring框架的Aop來實現,AOP面向切面編程,我們可以利用AOP做很多事情。那如果AOP遇到注解會發生什么那。
下一篇我們利用自定義注解和Aop實現接口防刷的功能。也就是在一段時間內如果大量訪問接口,就會觸發保護的一個案例。
End!!! 如有疑問請留言評論!
導航
接口防刷案例【傳送門】
總結
以上是生活随笔為你收集整理的啊啊,终于搞明白了,原来注解是这么一回事。6000+字理解注解【一】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电子科技大学计算机专业分班,关于电子科技
- 下一篇: 跳转指令: JMP、JECXZ、JA、J