Spring(2)-AOP簡介

莫小點還有救 發表於 2021-10-19
Spring

Spring AOP簡介

1. 定義

AOP是Aspect Oriented Programming,即面向切面程式設計。

那什麼是AOP?

我們先回顧一下OOP:Object Oriented Programming,OOP作為物件導向程式設計的模式,獲得了巨大的成功,OOP的主要功能是資料封裝、繼承和多型。

而AOP是一種新的程式設計方式,它和OOP不同,OOP把系統看作多個物件的互動,AOP把系統分解為不同的關注點,或者稱之為切面(Aspect)。

AOP在程式開發中主要用來解決一些系統層面上的問題,比如日誌,事務,許可權等待,Struts2的攔截器設計就是基於AOP的思想,是個比較經典的例子。

2.AOP原理

AOP需要解決的問題是,如何把切面織入到核心邏輯中,如何對呼叫方法進行攔截,並在攔截前後進行安全檢查、日誌、事務等處理,就相當於完成了所有業務功能。

在Java平臺上,對於AOP的織入,有3種方式:

  1. 編譯期:在編譯時,由編譯器把切面呼叫編譯進位元組碼,這種方式需要定義新的關鍵字並擴充套件編譯器,AspectJ就擴充套件了Java編譯器,使用關鍵字aspect來實現織入;
  2. 類載入器:在目標類被裝載到JVM時,通過一個特殊的類載入器,對目標類的位元組碼重新“增強”;
  3. 執行期:目標物件和切面都是普通Java類,通過JVM的動態代理功能或者第三方庫實現執行期動態織入。

最簡單的方式是第三種,Spring的AOP實現就是基於JVM的動態代理。由於JVM的動態代理要求必須實現介面,如果一個普通類沒有業務介面,就需要通過CGLIB或者Javassist這些第三方庫實現。

AOP技術看上去比較神祕,但實際上,它本質就是一個動態代理,讓我們把一些常用功能如許可權檢查、日誌、事務等,從每個業務方法中剝離出來。

需要特別指出的是,AOP對於解決特定問題,例如事務管理非常有用,這是因為分散在各處的事務程式碼幾乎是完全相同的,並且它們需要的引數(JDBC的Connection)也是固定的。另一些特定問題,如日誌,就不那麼容易實現,因為日誌雖然簡單,但列印日誌的時候,經常需要捕獲區域性變數,如果使用AOP實現日誌,我們只能輸出固定格式的日誌,因此,使用AOP時,必須適合特定的場景。

3. 語法

3.1 基本概念

  • Aspect:切面,即一個橫跨多個核心邏輯的功能,或者稱之為系統關注點;
  • Joinpoint:連線點,即定義在應用程式流程的何處插入切面的執行;
  • Pointcut:切入點,即一組連線點的集合;
  • Advice:增強,指特定連線點上執行的動作;
  • Introduction:引介,指為一個已有的Java物件動態地增加新的介面;
  • Weaving:織入,指將切面整合到程式的執行流程中;
  • Interceptor:攔截器,是一種實現增強的方式;
  • Target Object:目標物件,即真正執行業務的核心邏輯物件;
  • AOP Proxy:AOP代理,是客戶端持有的增強後的物件引用,Spring中的AOP代理可以使JDK動態代理,也可以是CGLIB代理,前者基於介面,後者基於子類

3.2 通知方法

  • 前置通知(@Before)

    • 在目標方法被呼叫之前做增強處理,@Before只需要指定切入點表示式即可
  • 後置通知(@After)

    • 在目標方法完成之後做增強,無論目標方法時候成功完成。@After可以指定一個切入點表示式
  • 返回通知 (@AfterReturning)

    • 在目標方法正常完成後做增強,@AfterReturning除了指定切入點表示式後,還可以指定一個返回值形參名returning,代表目標方法的返回值
  • 異常通知 (@AfterThrowing)

    • 主要用來處理程式中未處理的異常,@AfterThrowing除了指定切入點表示式後,還可以指定一個throwing的返回值形參名,可以通過該形參名來訪問目標方法中所丟擲的異常物件
  • 環繞通知 (@Around)

    • 環繞通知,在目標方法完成前後做增強處理,環繞通知是最重要的通知型別,像事務,日誌等都是環繞通知,注意程式設計中核心是一個ProceedingJoinPoint

3.3 啟用方式

  1. xml配置方式,在applicationContext.xml中配置下面一句:

    <aop:aspectj-autoproxy />
  2. 註解方式,在啟動類上加上@EnableAspectJAutoProxy

    @EnableAspectJAutoProxy
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            //do something
        }
    }

4.例項

4.1 定義目標類

public class MathCalculator {
    public int div(int x, int y) {
        System.out.println(x / y);
        return x / y;
    }
}

4.2 定義切面類,並指定通知方法

@Aspect
public class LogAspects {
    @Pointcut("execution(int com.test.tornesol.util.spring.spring_aop.MathCalculator.div(int,int))")
    public void pointCut() { }

    @Before("com.test.tornesol.util.spring.spring_aop.LogAspects.pointCut()")
    public void logStart(JoinPoint joinPoint) {
        System.out.println(joinPoint.getSignature().getName() + " 除法執行,引數是:" + Arrays.asList(joinPoint.getArgs()));
    }

    @After("com.test.tornesol.util.spring.spring_aop.LogAspects.pointCut()")
    public void logEnd() {
        System.out.println("除法結束");
    }


    @AfterReturning(value = "com.test.tornesol.util.spring.spring_aop.LogAspects.pointCut())", returning = "result")
    public void logReturn2(JoinPoint joinPoint, Object result) {
        System.out.println(joinPoint.getSignature().getName() + "除法返回" + result);
    }

    @AfterThrowing(value = "com.test.tornesol.util.spring.spring_aop.LogAspects.pointCut()", throwing = "exception")
    public void logException(Exception exception) {
        System.out.println("除法異常");
    }
}
Notes: 注意給切面類新增@Aspect註解

4.3 新增Configuration類,注入目標類和切面類,並開啟AOP代理模式

@Configuration
@EnableAspectJAutoProxy//開啟基於註解的AOP模式
public class MainConfig {
    @Bean
    public MathCalculator mathCalculator() {
        return new MathCalculator();
    }
    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }
}

4.4 測試 輸出

public class AopDemo {
    static public void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        context.getBean(MathCalculator.class).div(4, 2);
    }
}
div 除法執行,引數是:[4, 2]
2
除法結束
div除法返回2

5. 參考文件

  1. 使用AOP - 廖雪峰的官方網站 (liaoxuefeng.com),廖雪峰的文件寫的還不錯,可以用來快速瞭解上手AOP。
  2. Aspect Oriented Programming With Spring,Spring的官方文件最權威詳細