[Spring]Spring AOP基本概念介紹與入門

1. Spring容器解決了什么問題

優(yōu)點:

  1. 讓代碼架構(gòu)自上到下實現(xiàn)了低耦合.Spring支持接口注入,變更代碼邏輯時,只需要變更實現(xiàn)類即可做到無縫的切換.
  2. OOP將業(yè)務(wù)程序分解成各個層次的對象,通過對象聯(lián)動完成業(yè)務(wù)

缺點:

  1. 無法處理分散在各個業(yè)務(wù)之間的通用系統(tǒng)需求.例如:代碼日志、業(yè)務(wù)功能權(quán)限校驗、緩存、事務(wù)管理...容易發(fā)生代碼入侵,增加耦合度和維護成本.

2. 面向切面編程

Aspect Oriented Programming,面向切面編程,即我們常說的AOP.
它提供了一種在編譯期間和運行期間對某個功能進行增強的技術(shù),它是OOP的一種補充.在OOP中每個單元模塊為Class,而AOP關(guān)注的單元模塊為切面(Aspect).
舉個例子:@Transactional這個關(guān)于事務(wù)管理的注解,在方法上進行標記,那么在這么方法的開始和結(jié)束都會進行一些事務(wù)的操作.

橫切面

從圖上可以看到,當addUser加上@Transactional,其中涉及數(shù)據(jù)庫的操作就會被事務(wù)管理的類負責代理,進而實現(xiàn)功能上的增強.
Spring基于AspectJ開發(fā)了Spring AOP的框架,提供基于schema方式或者@AspectJ注解風格的切面編程能力.
其中,框架內(nèi)部提供了一系列聲明式的切面服務(wù):例如@Transactional、@Async@Cacheable...
另外,用戶如果想自定義自己的切面,也可以使用Spring AOP框架進行編程.

注意,AOP是一種思想,并不是Spring所獨有的.

2.1 切面編程中的概念

要了解AOP,需要先了解一下AOP中常說的概念:

  • Aspect: 切面,切面是AOP中封裝切面邏輯的模塊化單元.在Spring AOP中,切面往往以類或者XML的形式存在。
    打個比方,如果你需要對系統(tǒng)日志做一個切面處理,那么你就需要編寫一個類來描述其中的邏輯,那么這個類就是Aspect.
  • Join point: 連接點,指程序執(zhí)行中的一點。在Spring AOP中,連接點始終代表方法的執(zhí)行.
  • Advice: 通知,即在特定的連接點切面執(zhí)行的時機.Spring AOP提供了Around、Before、After等切面邏輯執(zhí)行的時機.
  • Pointcut: 切入點,連接點表示的是所有可以進行切面邏輯的地方,那么切入點則對應(yīng)切面邏輯需要切入的地方,Spring AOP支持使用AspectJ的切入點表達式來指定切入點.
    這里可能會有點繞,這里舉一個例子:假設(shè)一個類中有3個方法,那么這三個方法都可以作為切面的連接點,此時如果只需要對其中的一個方法做切面邏輯,那么這個方法就是我們的切入點了。
  • Introduction: 在某個被切面邏輯環(huán)繞的對象中引入新的接口.在Spring AOP中,可以通過introduction來將一個新的接口"引入"到被切入的類中.
  • Target object: 被切面邏輯環(huán)繞的對象。在Spring AOP中,被切面邏輯環(huán)繞的類通常是被代理的,也可以稱之為被代理的對象。
  • AOP proxy: 由AOP框架創(chuàng)建出的對象,為了去實現(xiàn)AOP邏輯.在Spring AOP中,支持兩種代理JDK動態(tài)代理CGLIB代理
  • Weaving: 將切面與被代理對象進行鏈接.Spring AOP在運行時執(zhí)行織入邏輯.
2.1.1 Spring AOP中的Advice
Advice(通知) 描述
Before advice 前置通知,在連接點之前執(zhí)行,不會影響整體的執(zhí)行流程,除非拋出異常.
After returning advice 后置通知,在連接點正常返回之后執(zhí)行(如果拋出了異常就不會執(zhí)行此通知).
After throwing advice 在某個連接點拋出異常后執(zhí)行
After (finally) advice 無論連接點是否正常執(zhí)行,均會執(zhí)行此通知(相當于try finally中的finally).
Around advice 環(huán)繞通知可以做到上述通知可以做到的事情.想象一下被代理的方法為a(),那么環(huán)繞可以對a()try catch finally,環(huán)繞通知是最常用的一種通知.

既然Around advice是覆蓋所有advice的,那么為什么Spring AOP還需要聲明這么多advice,官方的說法是建議使用最小的advice級別來滿足你的需求.
打個比方:如果僅僅需要記錄每個方法的入?yún)⒆鲆粋€log操作,那么使用before advice就已經(jīng)可以滿足了,而不需要使用到around advice.

2.1.2 advice執(zhí)行順序

先說結(jié)論:Spring從5.2.7后對@After的執(zhí)行順序進行了調(diào)整.如圖所示:
按官方的說法是跟隨AspectJ的語義.
點我前往

5.2.7

在5.2.7之前,Spring AOP按照@Around,@Before,After,AfterReturning,AfterThrowing的順序執(zhí)行.

before_5.2.7

2.2 開始簡單的AOP編程

  • 建立一個簡單的service作為被代理對象
package com.xjm.service.impl;

import com.xjm.service.HelloService;
import org.springframework.stereotype.Service;

/**
 * @author jaymin
 * 2020/11/26 17:04
 */
@Service
public class HelloServiceImpl implements HelloService {

    @Override
    public String hello() {
        return "Hello,Spring Framework!";
    }
}

  • 編寫切面類
package com.xjm.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.logging.Logger;

/**
 * @author jaymin. <br>
 * 系統(tǒng)基礎(chǔ)切面,主要用于研究AOP.
 * 2021/2/12 20:56
 */
@Aspect
@Component
public class SystemServiceAspect {

    private static final Logger log = Logger.getGlobal();

    /**
     * 使用常量統(tǒng)一管理切入點
     */
    public static final String SYSTEM_SERVICE_POINT_CUT = "systemServicePointCut()";

    /**
     * <p>pointcut可以使用表達式來指定切入點.
     * <p>execution中的內(nèi)容即為表達式.</p>
     * <p>* com.xjm..service..*.*(..)</p>
     * <p>表示,攔截com.xjm包下的service包中的所有類的所有方法(包括任意參數(shù))
     */
    @Pointcut("execution(* com.xjm..service..*.*(..))")
    public void systemServicePointCut() {
    }

    /**
     * 在方法執(zhí)行前進行切入
     * @param joinPoint
     */
    @Before(SYSTEM_SERVICE_POINT_CUT)
    public void before(JoinPoint joinPoint) {
        log.info("before method execute ");
    }

    /**
     * 環(huán)繞整個方法的執(zhí)行
     * @param joinPoint
     * @return
     */
    @Around(SYSTEM_SERVICE_POINT_CUT)
    public Object around(JoinPoint joinPoint) {
        LocalDateTime startTime = LocalDateTime.now();
        log.info("around  method starts ");
        Object result;
        try {
            result = ((ProceedingJoinPoint) joinPoint).proceed();
        } catch (Throwable throwable) {
            throw new RuntimeException(throwable);
        } finally {
            LocalDateTime endTime = LocalDateTime.now();
            long executeTime = Duration.between(startTime, endTime).toMillis();
            log.info("method end.");
        }
        return result;
    }

    /**
     * 在方法返回值后進行切入
     * @param joinPoint
     * @param result
     */
    @AfterReturning(pointcut = SYSTEM_SERVICE_POINT_CUT, returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        log.info("method return");
    }

    /**
     * 在方法拋出異常后進行切入
     * @param joinPoint
     * @param exception
     */
    @AfterThrowing(pointcut = SYSTEM_SERVICE_POINT_CUT, throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception) {
        log.info("current method throw an exception,message");
    }

    /**
     * 獲取連接點方法名
     * @param joinPoint
     * @return
     */
    public String getMethodName(JoinPoint joinPoint) {
        return ((MethodSignature) joinPoint.getSignature()).getMethod().getName();
    }

    /**
     * 在方法執(zhí)行后進行切入
     * @param joinPoint
     */
    @After(SYSTEM_SERVICE_POINT_CUT)
    public void after(JoinPoint joinPoint) {
        log.info("after method execute");
    }
}

  • 執(zhí)行hello方法

注意,Spring是默認不開啟AOP的,如果使用的是SpringBoot,需要在啟動類上加上@EnableAspectJAutoProxy.

Result:

二月 13, 2021 10:42:38 下午 com.xjm.aop.SystemServiceAspect around
信息: around  method starts 
二月 13, 2021 10:42:38 下午 com.xjm.aop.SystemServiceAspect before
信息: before method execute 
二月 13, 2021 10:42:38 下午 com.xjm.aop.SystemServiceAspect around
信息: method end.
二月 13, 2021 10:42:38 下午 com.xjm.aop.SystemServiceAspect after
信息: after method execute
二月 13, 2021 10:42:38 下午 com.xjm.aop.SystemServiceAspect afterReturning
信息: method return

筆者使用的是Spring 5.1.x版本

總結(jié)

  • Spring AOP以可插拔的形式提供了面向切面編程的框架能力,以便對OOP做進一步的增強.
  • AOP是另一種編程思想,它擁有成熟的一套體系和自己的術(shù)語.
  • AOP可以通過預(yù)編譯方式或者運行期動態(tài)代理的方式對程序功能進行織入.
  • Spring AOP僅支持方法執(zhí)行連接點,內(nèi)部使用了JDK動態(tài)代理和CGLIB代理對AOP進行支持.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容