自定義註解加AOP怎麼玩?
前言
註解是在JDK1.5
之後引入的新特性位於 java.lang.annotation
,註解其實就是對程式碼進行一種特殊的標記,這些標記可以在編譯,類載入和執行時被讀取,並執行相應的處理。本文主要分析如何自定義註解和註解的一些基礎知識,然後在配合這AOP
在實際運用中玩出新花樣。
本文分為三部分
- 註解分析
- 自定義註解
- 編譯時註解
- 執行時註解
- 整合AOP
原本可以分為兩篇文章,但是想來想去還是寫一篇。趁熱打鐵。
註解分析
註解怎麼執行的
想要自定義註解就要知道註解是怎麼構成的,結合著專案中常用的註解來分析一下註解到底是怎麼工作的。
看一下@Override
註解 其主要作用是編譯時進行格式檢查。點進去看一下@Override
點進去發現裡面是空的除了兩個元註解什麼都沒有,那麼它到底是怎麼實現的呢
其實@Override
可以理解為是一個標籤,它並沒有實際的邏輯處理,而實現邏輯的就是註解的使用者。它本質就是一個 『標記式註解』,僅被編譯器可知 。
舉個例子你的老闆讓你整理一下重要的檔案,但是檔案太多了你肯定需要把一下重要的檔案給標記出來,然後你交給你老闆的時候,老闆會怎麼做?老闆當然是看到有標記的檔案就去檢查一下。
結合著上面的例子使用@Override
註解的就是你,你的老闆就是 JVM虛擬機器器,在編譯的時候就是你的老闆進行檢查的時候,JVM發現了這個註解(標記)則就會進行處理 其處理機制主要是JVM內部處理。
總結下來就是:
定義註解,掃描註解,執行邏輯
元註解
在自定義註解之前我們要知道幾個JDK為我們提供的“元註解”,元註解就是定義註解的註解,下面看看都有什麼作用。
元註解一共有四個,都可以在 java.lang.annotation
下找到
- @Target
- @Retention
- @Documented
- @Inherited
@Target
@Target
註解主要用於定義註解使用的位置,被描述的註解可以用在什麼地方 。@Target
的引數是ElementType列舉類,下面詳解都有什麼作用。
列舉? |
作用? |
---|---|
ElementType.PACKAGE | 註解用在包 |
ElementType.TYPE | 註解作用於型別(類,介面,註解,列舉) |
ElementType.ANNOTATION_TYPE | 註解作用於註解 |
ElementType.CONSTRUCTOR | 註解作用於構造方法 |
ElementType.METHOD | 註解作用於方法 |
ElementType.PARAMETER | 註解作用於方法引數 |
ElementType.FIELD | 註解作用於屬性 |
ElementType.LOCAL_VARIABLE | 註解作用於區域性變數 |
@Target
如果不設定範圍的話預設可以作用於所有目標上面
看一下@Target
的原始碼
看一下里面有一個Value引數,它的返回值為ElementType[]
,ElementType就是上面的列舉類。
@Retention
@Retention註解的作用就是指定註解的生命週期。比如在編譯時可以處理執行時可以處理等。它的列舉類為RetentionPolicy
列舉? |
作用? |
---|---|
RetentionPolicy.SOURCE | 原始碼中保留,編譯期可以處理 |
RetentionPolicy.CLASS | Class檔案中保留,Class載入時可以處理 |
RetentionPolicy.RUNTIME | 執行時保留,執行中可以處理 |
@Retention
的預設值為 RetentionPolicy.CLASS
即在Class載入時處理
@Retention
原始碼
@Documented
@Documented註解的話就比較簡單,主要作用就是描述註解檔案化。就是在 在生成javadoc的時候,是不包含註釋的,但是如果註解被@Documented修飾,則生成的檔案就包含該註解。 此註解在以後版本可能會被刪除這裡就不詳細的看了。
@Inherited
@Inherited 註解修飾的註解時具有可繼承性的,就是說我們用 @Inherited 修飾了一個類,那麼這個類的子類也會預設繼承此註解。
原始碼
自定義註解
上面介紹了註解的元註解,那現在就開始實戰自定義註解。
GIT專案地址: https://github.com/scramblecode/project-demos
和往常套路一樣先建立專案,上面是本文章的示例可以下載下來看。
首先先寫一個簡單的例子。然後實戰在SpringBoot中使用自定義註解加攔截器獲取到請求引數。
簡單定義註解
這裡介紹兩個例子 一個是編譯時註解,第二個例子是執行時註解。最後在配合著SpringBoot+AOP寫一個專案中非常實用的例子
編譯時註解
建立編譯時註解我們首先要建立一個依賴專案作為註解處理器。
首先先建立一個註解介面,使用IDEA建立可以選擇建立註解。
在建立一個DataTest
註解,這裡定義註解的目的就是如果使用了該註解在編譯時打印出Hello World!
。
然後編寫註解處理器,這裡使用的AbstractProcessor
,本文只限簡單使用,如果有機會寫一篇文章研究AbstractProcessor
。
具體程式碼
然後需要建立META-INF檔案,這裡推薦使用谷歌的 auto-service
可以自動生成 META-INF/services/javax.annotation.processing.Processor
。
加入依賴即可
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc5</version>
</dependency>複製程式碼
定義完後在你的主專案引入註解處理器。在POM檔案中加入本地註解處理器的依賴
新增完成之後建立一個簡單的類,然後加上@DataTest
註解
執行開始編譯,就會發現控制檯輸出以下資訊。
編譯時註解可以寫一些生成工具比如lombok這種生成程式碼的工具可以使用。
執行時註解
簡單建立一個註解來獲取被註解標識的名稱和包路徑。
首先建立註解,定義為執行時註解目標為類屬性等。
使用註解
@GetClassName(value = "測試註解")
public class Student {
}複製程式碼
然後建立一個註解處理類,執行
控制檯輸出。
整合AOP
在Web開發中經常要輸出日誌,然後還有介面的執行時間。現在我們就用自定義註解加AOP實現這種功能。
首先把專案完善一下,增加一個測試介面
然後建立log註解。
然後定義切面類
@Aspect
@Component
@Slf4j
public class LoggerAspect {
private static final Logger logger = LoggerFactory.getLogger(LoggerAspect.class);
@Pointcut("@annotation(com.lqcoder.annotationdemo.annotation.OutputLog)")
public void weblog(){
}
@Around("weblog()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
List<Object> logArgs = Arrays.stream(point.getArgs())
.filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
.collect(Collectors.toList());
try {
logger.info("請求url={},請求引數={}",request.getRequestURI(),JSON.toJSONString(logArgs));
} catch (Exception e) {
logger.error("請求引數獲取異常",e);
}
Object result = point.proceed();
//執行時長(毫秒)
long time = System.currentTimeMillis() - beginTime;
try {
logger.info("請求耗時={}ms,返回結果={}",time,JSON.toJSONString(result));
} catch (Exception e) {
logger.error("返回引數獲取異常",e);
}
return result;
}
}複製程式碼
定義好之後重啟專案,然後呼叫一下介面
執行結果可以看到已經生效。
本文結束。