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。