Spring源碼-AOP(三)Springboot與Aop

前言

把文章在簡書同步了之后,有幾篇文章被收入專題,還有一篇文章被收入首頁,盡管不是什么了不起的事,但是這樣也讓自己挺開心的,慕課網約我錄了一次視頻課程,沒有通過,讓修改下錄第二次,想想還是算了,目前的自己水平還是有限,另一方面時間也比較有限,在博客這方面能夠持續下去就已經是件很難的事情,其他的以后再說看緣分吧。接下來繼續技術的分析。

前兩篇主要記錄了AOP所用的核心設計模式-代理模式,包含動態代理和靜態代理,以及JDK和CGLIB的兩種實現方式,接下來開始重點分析Spring-AOP源碼相關操作,當然開始也是從概念理解。這個不太好明白,但是第一遍閱讀也主要是為了對于框架有個大局觀,不需要過度的拘泥于細節。AOP這段計劃在9月中之前結束,而下半月主要是SpringMVC相關的源碼研究,可能也會結合一點事務和mybatis源碼的研究。

參考了很多程序員DD的東西

Spring-Aop相關概念如下:


我把這個圖片放在網絡上了,歡迎大家前往下載,建議大家先從地址一下載,地址二要耗流量
下載地址
備用地址
相關的概念描述在腦圖中有詳細解釋,當然方便理解,這里我也會記錄下來。

  • Target(目標對象):需要被代理增強的對象
  • Proxy(代理對象):目標對象被AOP 織入 增強/通知后,產生的對象.
  • Joinpoint(連接點):指那些被攔截到的點.在Spring中,這些點指方法(因為Spring只支持方法類型的連接點).
  • Pointcut(切入點):指需要(配置)被增強的Joinpoint.
  • Advice(通知/增強):指攔截到Joinpoint后要做的操作.通知分為前置通知/后置通知/異常通知/最終通知/環繞通知等.
  • Aspect(切面):切入點和通知的結合。
  • Weaving(織入):指把增強/通知應用到目標對象來創建代理對象的過程(Spring采用動態代理織入,AspectJ采用編譯期織入和類裝載期織入).
  • Introduction(引入增強):一種特殊通知,在不修改類代碼的前提下,可以在運行期為類動態地添加一些Method/Field(不常用).

項目部署

我們先在springboot中將aop整合進來
在pom.xml引入pom依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

starter中默認添加了@EnableAspectJAutoProxy
設計一個簡單的controller入門

@RestController
public class HomeController {
    private Logger logger = Logger.getLogger(getClass());
    @GetMapping("/index")
    public String index(@RequestParam String name){
        logger.info("-----------{name}:{}"+name);
        return "【welcome to aop】:" +name;
    }
}

實現Web層的日志切面

  • 使用@Aspect注解將一個java類定義為切面類
  • 使用@Pointcut定義一個切入點,可以是一個規則表達式,比如下例中某個package下的所有函數,也可以是一個注解等。根據需要在切入點不同位置的切入內容
  • 使用@Before在切入點開始處切入內容
  • 使用@After在切入點結尾處切入內容
  • 使用@AfterReturning在切入點return內容之后切入內容(可以用來對處理返回值做一些加工處理)
  • 使用@Around在切入點前后切入內容,并自己控制何時執行切入點自身的內容
  • 使用@AfterThrowing用來處理當切入內容部分拋出異常之后的處理邏輯
    代碼如下:
@Aspect
@Component
public class WebLogAspect {
    private Logger logger = Logger.getLogger(getClass());
    @Pointcut("execution(public * com.sunliangliang.springsource.controller..*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到請求,記錄請求內容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 記錄下請求內容
        logger.info("URL : " + request.getRequestURL().toString());
        logger.info("HTTP_METHOD : " + request.getMethod());
        logger.info("IP : " + request.getRemoteAddr());
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
    }
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 處理完請求,返回內容
        logger.info("RESPONSE : " + ret);
    }
}
  • 通過@Pointcut定義切入點為com.sunliangliang.springsource.controller.包下面的所有函數,然后通過@Before實現,對請求內容的日志記錄(本文只是說明過程,可以根據需要調整內容),最后通過@AfterReturning記錄請求返回的對象。

運行程序

輸出如下日志:

2017-09-01 18:20:34.953  INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect        : URL : http://localhost:8888/index
2017-09-01 18:20:34.953  INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect        : HTTP_METHOD : GET
2017-09-01 18:20:34.953  INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect        : IP : 0:0:0:0:0:0:0:1
2017-09-01 18:20:34.953  INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect        : CLASS_METHOD : com.sunliangliang.springsource.controller.HomeController.index
2017-09-01 18:20:34.953  INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect        : ARGS : [liangliang]
2017-09-01 18:20:34.953  INFO 14696 --- [nio-8888-exec-3] c.s.s.controller.HomeController          : -----------{name}:{}liangliang
2017-09-01 18:20:34.953  INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect        : RESPONSE : 【welcome to aop】:liangliang

優化

由于通過AOP實現,程序得到了很好的解耦,但是也會帶來一些問題,比如:我們可能會對Web層做多個切面,校驗用戶,校驗頭信息等等,這個時候經常會碰到切面的處理順序問題。
所以要定義每個切面的優先級,我們需要@Order(i)注解來標識切面的優先級。i值越小,優先級越高。假設我們還有一個切面是CheckNameAspect用來校驗name必須為didi,我們為其設置@Order(10),而上文中WebLogAspect設置為@Order(5),所以WebLogAspect有更高的優先級,這個時候執行順序是這樣的:

  • 在@Before中優先執行@Order(5)的內容,再執行@Order(10)的內容

  • 在@After和@AfterReturning中優先執行@Order(10)的內容,再執行@Order(5)的內容
    所以我們可以這樣子總結:

  • 在切入點前的操作,按order的值由小到大執行

  • 在切入點后的操作,按order的值由大到小執行
    代碼完整示例

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 本章內容: 面向切面編程的基本原理 通過POJO創建切面 使用@AspectJ注解 為AspectJ切面注入依賴 ...
    謝隨安閱讀 3,193評論 0 9
  • AOP實現可分為兩類(按AOP框架修改源代碼的時機): 靜態AOP實現:AOP框架在編譯階段對程序進行修改,即實現...
    數獨題閱讀 2,340評論 0 22
  • AOP,也就是面向方面編程或者說面向面編程,是一種很重要的思想。在企業級系統中經常需要打印日志、事務管理這樣針對某...
    樂百川閱讀 915評論 0 8
  • 當心態開始轉變時,就產生了正念的奇跡,雖然也還是會絮絮叨叨,但是程度已經有明顯的變化。 生活方面也已經有了變化,...
    三月Flora閱讀 155評論 0 0
  • 《職場媽媽提升術》第一課 為母則強,你真的強大了嗎? 再忙也要成長 再忙也要快樂 再忙也要美好 歡迎來到“職場媽媽...
    萱萱媽媽說閱讀 255評論 0 0