原文地址html
註解是在JDK1.5
以後引入的新特性位於 java.lang.annotation
,註解其實就是對代碼進行一種特殊的標記,這些標記能夠在編譯,類加載和運行時被讀取,並執行相應的處理。本文主要分析如何自定義註解和註解的一些基礎知識,而後在配合這AOP
在實際運用中玩出新花樣。java
本文分爲三部分git
本來能夠分爲兩篇文章,可是想來想去仍是寫一篇。趁熱打鐵。github
想要自定義註解就要知道註解是怎麼構成的,結合着項目中經常使用的註解來分析一下註解究竟是怎麼工做的。web
看一下@Override
註解 其主要做用是編譯時進行格式檢查。點進去看一下@Override
實現。ide
點進去發現裏面是空的除了兩個元註解什麼都沒有,那麼它究竟是怎麼實現的呢工具
其實@Override
能夠理解爲是一個標籤,它並無實際的邏輯處理,而實現邏輯的就是註解的用戶。它本質就是一個 『標記式註解』,僅被編譯器可知 。測試
舉個例子你的老闆讓你整理一下重要的文檔,可是文檔太多了你確定須要把一下重要的文檔給標記出來,而後你交給你老闆的時候,老闆會怎麼作?老闆固然是看到有標記的文檔就去檢查一下。google
結合着上面的例子使用@Override
註解的就是你,你的老闆就是 JVM虛擬機,在編譯的時候就是你的老闆進行檢查的時候,JVM發現了這個註解(標記)則就會進行處理 其處理機制主要是JVM內部處理。url
總結下來就是:
定義註解,掃描註解,執行邏輯
在自定義註解以前咱們要知道幾個JDK爲咱們提供的「元註解」,元註解就是定義註解的註解,下面看看都有什麼做用。
元註解一共有四個,均可以在 java.lang.annotation
下找到
@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註解的做用就是指定註解的生命週期。好比在編譯時能夠處理運行時能夠處理等。它的枚舉類爲RetentionPolicy
枚舉😀 |
做用😊 |
---|---|
RetentionPolicy.SOURCE | 源碼中保留,編譯期能夠處理 |
RetentionPolicy.CLASS | Class文件中保留,Class加載時能夠處理 |
RetentionPolicy.RUNTIME | 運行時保留,運行中能夠處理 |
@Retention
的默認值爲 RetentionPolicy.CLASS
即在Class加載時處理
@Retention
源碼
@Documented註解的話就比較簡單,主要做用就是描述註解文檔化。就是在 在生成javadoc的時候,是不包含註釋的,可是若是註解被@Documented修飾,則生成的文檔就包含該註解。 此註解在之後版本可能會被刪除這裏就不詳細的看了。
@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 {
}複製代碼
而後建立一個註解處理類,運行
控制檯輸出。
在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;
}
}複製代碼
定義好以後重啓項目,而後調用一下接口
運行結果能夠看到已經生效。
本文結束。