你不是說你會Aop嗎?

一大早,小王就急匆匆的跑過來找我,說:周哥,那個記錄日誌的功能我想請教一下。java

由於公司某個項目要跟別的平臺作對接,咱們這邊須要給他們提供一套接口。昨天,我就將記錄接口日誌的工做安排給了小王。web

下面是我跟小王的主要對話。app

我:說說怎麼了?ide

小王:我將記錄接口日誌的功能放到了每一個controller中,如今感受有點繁瑣,我這樣作是否是不太合適?post

我:爲何要去每一個接口裏記錄日誌?優化

小王:最開始我是用的攔截器,可是這樣一個請求就記錄了兩條記錄。加密

我:爲何是兩條?日誌

小王:在preHandle中記錄一條請求數據,在postHandle中記錄一條響應數據。code

我:。。。你不是說你會Aop嗎?對象

小王:Aop也是同樣,在前置通知記錄一條請求數據,後置通知記錄一條響應數據。

小王:這個數據和之前記錄操做日誌的不太同樣,之前只須要在前置通知記錄一條操做日誌就能夠了,可是如今有響應,因此只能在controller中記錄日誌了。

我:那你知不知道有個環繞通知?你說一下Aop就幾種通知類型。

小王:總共有五種,分別是:

  • 前置通知:在咱們執行目標方法以前運行(@Before
  • 後置通知:在咱們目標方法運行結束以後,無論有沒有異常(@After
  • 返回通知:在咱們的目標方法正常返回值後運行(@AfterReturning
  • 異常通知:在咱們的目標方法出現異常後運行(@AfterThrowing
  • 環繞通知:目標方法的調用由環繞通知決定,即你能夠決定是否調用目標方法,joinPoint.procced()就是執行目標方法的代碼 。環繞通知能夠控制返回對象(@Around)

接下來,咱們一塊兒來演示一下如何使用環繞通知來解決小王的問題。

第一步:提供接口用來接收參數和響應接口

@RestController
public class TestController {
    @GetMapping("/getName")
    public String getName(HttpServletRequest request) throw Exception {

        String result = "Java旅途";
        String age = request.getParameter("age");
        if("18".equals(age)){
            result = "沒法識別";
        }
        return result;
    }
}

第二步:定義切點

execution()是比較經常使用的定義切點的表達式,execution()語法以下:

execution(修飾符  返回值  包.類.方法名(參數) throws異常)

其中:

修飾符和throws異常能夠省略不寫

根據這些解釋,咱們能夠將第一步中的接口用execution()表達式來描述一下:

execution(String binzh.website.controller.TestController.GetName(HttpServletRequest))
  • *:匹配全部項

  • ..:匹配任意個方法參數
  • ..出如今類名中時,後面必須跟*,表示包、子孫包下的全部類;

如今咱們優化一下上面的表達式,定義切面爲controller包及controller下面全部包的全部方法

execution(* binzh.website.controller..*.*(..))

第三步:環繞通知記錄日誌

@Around("execution(* binzh.website.controller..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) {
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();
    String age = request.getParameter("age");
    Object proceed = "";
    try {
        proceed = joinPoint.proceed();
    } catch (Throwable e) {
        e.printStackTrace();
    }
    System.out.println("age==="+age);
    System.out.println("proceed ===="+proceed);
    return proceed;
}

運行結果以下:

age===19
proceed ====Java旅途

咱們之因此能夠用環繞通知來處理小王的問題。其中一個重要的緣由就是,咱們提供的全部接口都是通過統一加密的,最後請求的參數都是一個固定的名字。還須要注意的一點就是,環繞通知的返回值類型必須大於等於方法的返回值,即:加入你方法返回String類型,環繞通知不能寫成void類型

小王看到這裏後,恍然大悟,準備趕忙回去試一下。我急忙拉住他。

我:若是接口出現異常了怎麼辦?

小王:那我在異常通知裏處理就能夠了。

我:你再想一下?

小王:好像不行,異常通知裏獲取不到請求參數。

我:在環繞通知中捕獲處理能夠嗎?

這時候,看見小王眼睛發光,驚訝的說了一句:環繞通知太牛批了,居然能夠完成前置通知、後置通知和異常通知的工做!

這篇文章戲有點多,別見怪。實戰是提高技術最有效的途徑!

你不是說你會Aop嗎?

相關文章
相關標籤/搜索