自定義註解加AOP怎麼玩?

原文地址html

前言

註解是在JDK1.5以後引入的新特性位於 java.lang.annotation ,註解其實就是對代碼進行一種特殊的標記,這些標記能夠在編譯,類加載和運行時被讀取,並執行相應的處理。本文主要分析如何自定義註解和註解的一些基礎知識,而後在配合這AOP在實際運用中玩出新花樣。java

本文分爲三部分git

  • 註解分析
  • 自定義註解
    • 編譯時註解
    • 運行時註解
  • 整合AOP

本來能夠分爲兩篇文章,可是想來想去仍是寫一篇。趁熱打鐵。github

註解分析

註解怎麼運行的

想要自定義註解就要知道註解是怎麼構成的,結合着項目中經常使用的註解來分析一下註解究竟是怎麼工做的。web

看一下@Override註解 其主要做用是編譯時進行格式檢查。點進去看一下@Override實現。ide

點進去發現裏面是空的除了兩個元註解什麼都沒有,那麼它究竟是怎麼實現的呢工具

其實@Override能夠理解爲是一個標籤,它並無實際的邏輯處理,而實現邏輯的就是註解的用戶。它本質就是一個 『標記式註解』,僅被編譯器可知 。測試

舉個例子你的老闆讓你整理一下重要的文檔,可是文檔太多了你確定須要把一下重要的文檔給標記出來,而後你交給你老闆的時候,老闆會怎麼作?老闆固然是看到有標記的文檔就去檢查一下。google

結合着上面的例子使用@Override註解的就是你,你的老闆就是 JVM虛擬機,在編譯的時候就是你的老闆進行檢查的時候,JVM發現了這個註解(標記)則就會進行處理 其處理機制主要是JVM內部處理。url

總結下來就是:

定義註解,掃描註解,執行邏輯

元註解

在自定義註解以前咱們要知道幾個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;
    }

}複製代碼

定義好以後重啓項目,而後調用一下接口

運行結果能夠看到已經生效。

本文結束。

相關文章
相關標籤/搜索