自定義註解在開發中是一把利器,常常會被使用到。在上一篇文章中有提到了自定義校驗註解的用法。 然而最近接到這樣一個需求,主要是針對某些接口的返回數據須要進行一個加密操做。因而很天然的就想到了自定義註解+AOP去實現這樣一個功能。可是對於自定義註解,只是停留在表面的使用,沒有作到知其然,而知其因此然。因此這篇文章就是來了解自定義註解這把開發利器的。java
官方定義segmentfault
An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.app
Google翻譯一下jvm
註解是元數據的一種形式,能夠添加到Java源代碼中。 類,方法,變量,參數和包均可以被註釋。 註解對其註釋的代碼的操做沒有直接影響。ide
看完這個定義是否是有點摸不到頭腦,不要慌實踐出真知。
咱們先回顧一下需求的場景,是要針對xx接口的返回數據須要作一個加密操做。以前說到使用自定義註解+AOP來實現這個功能。因此咱們先定義一個註解叫Encryption
,被Encryption
註解修飾後接口,返回的數據要被加密。函數
public @interface Encryption { }
你會發現建立自定義註解,就和創建普通的接口同樣簡單。只是所使用的關鍵字有所不一樣。在底層實現上,全部定義的註解都會自動繼承java.lang.annotation.Annotation接口。測試
@Encryption @GetMapping("/encrypt") public ResultVo encrypt(){ return ResultVoUtil.success("不同的科技宅"); } @GetMapping("/normal") public ResultVo normal(){ return ResultVoUtil.success("不同的科技宅"); }
@Around("@annotation(com.hxh.unified.param.check.annotation.Encryption)") public ResultVo encryptPoint(ProceedingJoinPoint joinPoint) throws Throwable { ResultVo resultVo = (ResultVo) joinPoint.proceed(); // 獲取註解 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); Encryption annotation = method.getAnnotation(Encryption.class); // 若是被標識了,則進行加密 if(annotation != null){ // 進行加密 String encrypt = EncryptUtil.encryptByAes(JSON.toJSONString(resultVo.getData())); resultVo.setData(encrypt); } return resultVo; }
這個時候,你會發現返回的數據並無被加密。 那麼這個是爲啥呢?俗話說遇到問題不要慌,先掏出手機發個朋友圈(稍微有點跑題了)。出現這個緣由是,缺乏了@Retention
對@Encryption
的修飾,讓咱們把它加上。加密
@Retention(RetentionPolicy.RUNTIME) public @interface Encryption { }
繼續測試spa
這個時候返回的數據就被加密了,說明自定義註解生效了。翻譯
測試普通接口
沒有用@Encryption
的接口,返回的數據沒有被加密。到此需求就已經實現了,接下來就該瞭解原理了。
@Retention做用是什麼
Retention的翻譯過來就是"保留"的意思。也就意味着它的做用是,用來定義註解的生命週期的,而且在使用時須要指定RetentionPolicy
,RetentionPolicy
有三種策略,分別是:
選擇合適的生命週期
首先要明確生命週期 RUNTIME > CLASS > SOURCE 。通常若是須要在運行時去動態獲取註解信息,只能使用RUNTIME。若是要在編譯時進行一些預處理操做,好比生成一些輔助代碼就用CLASS。若是隻是作一些檢查性的操做,好比 @Override和@SuppressWarnings,則可選用 SOURCE。
咱們實際開發中的自定義註解幾乎都是使用的RUNTIME
最開始@Encryption
沒有使用@Retention
對其生命週期進行定義。因此致使AOP在獲取的時候一直爲空,若是爲空就不會對數據進行加密。
是否是感受這個註解太簡陋。那再給他加點東西,加上個@Target
。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Encryption { }
@Target
註解是限定自定義註解可使用在哪些地方。這就和參數校驗同樣,約定好規則,防止亂用而致使問題的出現。針對上述的需求能夠限定它只能用方法上。根據不一樣的場景,還可使用在更多的地方。好比說屬性、包、構造器上等等。
上面兩個是比較經常使用的元註解,Java一共提供了4個元註解。你可能會問元註解是什麼?元註解的做用就是負責註解其餘註解。
@Documented
的做用是對自定義註解進行標註,若是使用@Documented
標註了,在生成javadoc的時候就會把@Documented
註解給顯示出來。沒什麼實際做用,瞭解一下就行了。
被@Inherited
修飾的註解,被用在父類上時其子類也擁有該註解。 簡單的說就是,當在父類使用了被@Inherited
修飾的註解@InheritedTest
時,繼承它的子類也擁有@InheritedTest
註解。
這個能夠單獨講下
參照咱們在定義接口的經驗,在接口中能定義方法和常量。可是在自定義註解中,只能定義一個東西:註解類型元素Annotation type element
。
其實能夠簡單的理解爲只能定義方法,可是和接口中的方法有區別。
定義註解類型元素時須要注意以下幾點:
default
關鍵字設置"默認值"。繼續改造
需求這個東西常常都在變更。本來須要加密的接口只使用AES進行加密,後面又告知有些接口要使用DES加密。針對這樣的狀況,咱們能夠在註解內,添加一下配置項,來選擇使用何種方式加密。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Encryption { /** * 加密類型 */ String value() default "AES"; }
調整接口
@Encryption @GetMapping("/encrypt") public ResultVo encrypt(){ return ResultVoUtil.success("不同的科技宅"); } @Encryption(value = "DES") @GetMapping("/encryptDes") public ResultVo encryptDes(){ return ResultVoUtil.success("不同的科技宅"); } @GetMapping("/normal") public ResultVo normal(){ return ResultVoUtil.success("不同的科技宅"); }
調整AOP
@Around("@annotation(com.hxh.unified.param.check.annotation.Encryption)") public ResultVo encryptPoint(ProceedingJoinPoint joinPoint) throws Throwable { ResultVo resultVo = (ResultVo) joinPoint.proceed(); // 獲取註解 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); Encryption annotation = method.getAnnotation(Encryption.class); // 若是被標識了,則進行加密 if(annotation != null){ // 進行加密 String encrypt = null; switch (annotation.value()){ case "AES": encrypt = EncryptUtil.encryptByAes(JSON.toJSONString(resultVo.getData())); break; case "DES": encrypt = EncryptUtil.encryptByDes(JSON.toJSONString(resultVo.getData())); break; default: break; } resultVo.setData(encrypt); } return resultVo; }
至此就改造完了。能夠發現註解元素類型,在使用的時候,操做元素類型像在操做屬性。解析的時候,操做元素類型像在操做方法。。
@Encryption
和@Encryption()
等效。@Encryption("DES")
和@Encryption(value = "DES")
等效。@Retention
。@Target
來限制註解的使用範圍,防止註解被亂用。註解能夠理解爲就是一個標識。能夠在程序代碼中的關鍵節點上打上這些標識,它不會改變原有代碼的執行邏輯。而後程序在編譯時或運行時能夠檢測到這些標記,在作出相應的操做。結合上面的小場景,能夠得出自定義註解使用的基本流程:
若是以爲對你有幫助,能夠多多評論,多多點贊哦,也能夠到個人主頁看看,說不定有你喜歡的文章,也能夠隨手點個關注哦,謝謝。
我是不同的科技宅,天天進步一點點,體驗不同的生活。咱們下期見!