Spring AOP保姆級超詳細解析(上)
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
<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。