Spring AOP保姆級超詳細解析(上)

語言: CN / TW / HK

theme: channing-cyan highlight: a11y-dark


持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第9天,點選檢視活動詳情

前言

經過我們之前的學習我們對IoC有了一定的瞭解,並已經學會了IoC的基本使用。接下來,我們將要學習Spring另外一個核心機制————AOP。

AOP

為什麼要學習AOP?

AOP全稱是Aspect Oriented Programming,意思是面向切面程式設計

AOP是對面向物件程式設計的一個補充,在執行時,動態地將程式碼切入到類的指定方法、指定位置上的程式設計思想就是面向切面程式設計。將不同方法的同一個位置抽象成一個切面物件,對該切面物件進行程式設計就是AOP。

AOP的優點: - 使系統更加容易擴充套件。 - 更好的提高程式碼的複用性。 - 降低程式碼模組之間的耦合度。 - 使業務程式碼更加簡潔純粹,不參雜其他的非業務程式碼的影響。 - 使非業務程式碼更加集中,與業務程式碼區分開來,不分散,便於統一管理。

案例分析

我們先寫一個不使用AOP的案例

1.首先建立一個Maven工程。

(不懂Maven的小夥伴移步到我這個專欄的第一篇文章

2.在pom.xml檔案中引入Spring的AOP依賴。 ```js org.springframework spring-aop 5.2.10.RELEASE

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.0.11.RELEASE</version>
</dependency>

3.定義一個實現兩個數加減的介面。js package com;

public interface Cal { public int add(int num1,int num2); public int sub(int num1,int num2); } ``` 4.實現方法。

```js package com.impl;

import com.Cal;

public class CalImpl implements Cal { public int add(int num1, int num2) { System.out.println("執行加法,引數是("+num1+","+num2+")"); int result = num1 + num2; System.out.println("計算結果是:"+result); return result; }

public int sub(int num1, int num2) {
    System.out.println("執行減法,引數是("+num1+","+num2+")");
    int result = num1 - num2;
    System.out.println("計算結果是:"+result);
    return result;
}

} 5.呼叫方法。js Cal cal = new CalImpl(); cal.add(1,2); cal.sub(20,10); ``` 列印結果:

js 執行加法,引數是(1,2) 計算結果是:3 執行減法,引數是(20,10) 計算結果是:10

這樣是我們不使用AOP的一個案例,這樣我們的業務程式碼和列印日誌的程式碼就會混雜在一起,並且兩個方法有重複的部分,這時候我們就可以使用AOP將其列印日誌的程式碼提取出來統一管理。這就是面向切面程式設計的一個思想。

如何實現AOP?

使用動態代理來實現AOP,日誌資訊的列印可以交給代理去做,由代理統一管理,提高程式碼的可擴充套件性維護性

代理就類似於我們現實生活中的中介。

什麼是動態代理呢?動態代理就是在我們程式執行過程中,動態建立的一個代理類。

我們接下來用動態代理類來實現AOP。

1.建立MyInvocationHandler類。 ```js package com;

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays;

public class MyInvocationHandler implements InvocationHandler { //接受委託物件 private Object object = null;

//返回代理物件
public Object bind(Object object){
    this.object = object;
    return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
}

public Object invoke(Object prox, Method method,Object[] args)throws Throwable{
    System.out.println(method.getName()+"方法的引數是"+ Arrays.toString(args));
    Object result = method.invoke(this.object,args);
    System.out.println(method.getName()+"方法的結果是"+result);
    return result;
}

} ``MyInvocationHandler並不是我們上面所說的動態代理類,這個類是用來建立我們動態代理類的一個類。這裡可能會有點繞。我們這裡通過實現InvocationHandler`這個介面來完成動態代理的功能。

Spring使用Proxy.newProxyInstance類載入器來實現動態代理類的建立。使用object.getClass().getInterfaces()獲取委託物件的全部方法。

invoke方法介紹

在代理例項上處理方法呼叫並返回結果, 當在與之關聯的代理例項上呼叫方法時,將在呼叫處理程式中呼叫此方法。我們的日誌列印資訊就寫在invoke方法之中。

引數 - proxy:在其上呼叫方法的代理例項,也就是動態代理類。 - method: 對應於在代理例項上呼叫的介面方法的 Method 例項。 Method 物件的宣告類將是在其中宣告方法的介面,該介面可以是代理類賴以繼承方法的代理介面的超介面。 - args:包含傳入代理例項上方法呼叫的引數值的物件陣列,如果介面方法不使用引數,則為 null。基本型別的引數被包裝在適當基本包裝器類(如 java.lang.Integer 或 java.lang.Boolean)的例項中。

2.通過操作代理物件來呼叫。 js Cal cal = new CalImpl(); MyInvocationHandler myInvocationHandler = new MyInvocationHandler(); Cal cal1 = (Cal) myInvocationHandler.bind(cal); cal1.add(1,2); cal1.sub(20,10);

通過myInvocationHandler中的bind方法,我們傳一個委託物件進去,會返回一個代理物件。從而實現呼叫代理物件的方法。

3.將業務程式碼中之前的日誌列印資訊刪除。

列印結果:

js add方法的引數是[2, 5] add方法的結果是7 sub方法的引數是[7, 4] sub方法的結果是3 這樣我們就使用AOP實現了相同功能,並且將業務程式碼和非業務程式碼分開,模組之間的耦合性降低了,程式碼也更加的簡潔。

總結

我們面向切面程式設計是通過反射機制來實現的,我們學習Spring的IoC和AOP更多的是學習一種程式設計思想,理解了這種思想,技術自然就水到渠成。這就是通過動態代理的方式來實現AOP的過程了。下一篇文章講解下通過面向物件的方式來實現AOP。