通過例子全面解析Java中的Aop技術-包括靜態代理,動態代理,Spring Aop(全面解析,附帶全部原始碼,小白看一遍也可以理解)
阿新 • • 發佈:2019-02-16
AOP概述(Aspect Oriented Programming)
所謂AOP,就是面向方面(切面)的程式設計,簡單來說,就是通過面向切面,在執行的方法前後加上所需要實現的事情,比如,日誌,計算方法執行的時間,實現事務等。這樣做的目的一方面在於不改變原有程式碼,提高通用性,最重要的是通過切面可以在需要的地方加上所需要做的任何事情。
我們通過最簡單例子來說明
這裡我們要實現在執行加減法運算的前後,分別打印出提示資訊
首先定義一個加減運算的介面
package com.markorg.top.general; /** * @Author Mark * * @Date 2018年6月10日 * * @Describe:定義進行計算的介面 */ public interface ICalculate { public int plus(int x, int y); public int minus(int x, int y); }
寫出方法的實現
package com.markorg.top.general; /** * @Author Mark * * @Date 2018年6月10日 * * @Describe:加減法的實現類 */ public class Calculate implements ICalculate { /** * @加法運算 */ public int plus(int x, int y) { int m = x + y; System.out.println(m); return m; } /** * @減法運算 */ public int minus(int x, int y) { int n = x - y; System.out.println(n); return n; } }
接下來實現在計算前後在控制檯打印出資訊
第一種方式,直接在呼叫方法的時候進行列印
package com.markorg.top.general; /** * @Author Mark * * @Date 2018年6月10日 * * @Describe:不用切面的方法 */ public class General { public static void main(String[] args) { ICalculate calculate = new Calculate(); System.out.println("加法執行前"); calculate.plus(10, 1); System.out.println("加法執行後"); System.out.println("減法執行前"); calculate.minus(20, 12); System.out.println("減法執行後"); } }
方法執行後控制檯列印的結果
加法執行前
11
加法執行後
減法執行前
8
減法執行後
這種方法簡單粗暴,但是每次實現的時候都要寫,程式碼複用性差
第二種方式,我們使用代理模式來實現
package com.markorg.top.general;
/**
* @Author Mark
*
* @Date 2018年6月10日
*
* @Describe:靜態代理實現
*/
public class StaticCalculateProxy implements ICalculate {
ICalculate ica = new Calculate();
public int plus(int x, int y) {
System.out.println("方法執行前的切面");
int re = ica.plus(x, y);
System.out.println("方法執行後的切面");
return re;
}
public int minus(int x, int y) {
System.out.println("方法執行前的切面");
int re = ica.minus(x, y);
System.out.println("方法執行後的切面");
return re;
}
public static void main(String[] args) {
ICalculate ic = new StaticCalculateProxy();
ic.plus(10, 2);
ic.minus(20, 12);
}
}
執行的結果方法執行前的切面
12
方法執行後的切面
方法執行前的切面
8
方法執行後的切面
通過對比我們發現,使用了代理之後,它會幫我們實現切面中加入操作,每次呼叫加減運算的時候,我們只需要呼叫代理類的實現方法就可以加上切面了。第三種方式,我們使用JDK的動態代理來實現
雖然說第二種方法相對第一種來說,提高了複用性,不用每次都去手動加入切面,但是它有個問題,每次都需要new實現類的物件,在代理方法中才呼叫加減運算,有時候也會不方便。
下面是動態代理的實現類package com.markorg.top.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.markorg.top.general.Calculate;
import com.markorg.top.general.ICalculate;
/**
* @Author Mark
*
* @Date 2018年6月10日
*
* @Describe:使用JDK的動態代理來實現
*/
public class DynamicCalculate implements InvocationHandler {
Calculate c;
/**
* 使用asm生產被代理代理物件
*
* @param c
* @return
*/
public Object getProxy(Calculate c) {
this.c = c;
return Proxy.newProxyInstance(c.getClass().getClassLoader(), c.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----jdk動態代理切面執行前");
Object result = method.invoke(c, args);
System.out.println("-----jdk動態代理切面執行後");
return result;
}
public static void main(String[] args) {
ICalculate ic = (ICalculate) new DynamicCalculate().getProxy(new Calculate());
ic.plus(10, 8);
ic.minus(20, 12);
}
}
下面是執行後控制檯的結果
-----jdk動態代理切面執行前
18
-----jdk動態代理切面執行後
-----jdk動態代理切面執行前
8
-----jdk動態代理切面執行後
第四種 使用cglib動態代理來實現,這個時候被代理類可以不需要實現介面,因為是通過繼承的方法,當然,你想實現介面也是可以的下面是程式碼
package com.markorg.top.dynamic;
import java.lang.reflect.Method;
import com.markorg.top.general.Calculate;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* @Author Mark
*
* @Date 2018年6月10日
*
* @Describe:使用cglib實現動態代理
*/
public class CglibDynamicCalculate implements MethodInterceptor {
Object targetObject;
/**
* cglib生成所需要的業務類物件,供代理來使用
*
* @param target
* @return
*/
public Object getProxyObject(Object object) {
this.targetObject = object;
// 增強器,動態程式碼生成器
Enhancer enhancer = new Enhancer();
// 回撥方法
enhancer.setCallback(this);
// 設定生成類的父類型別
enhancer.setSuperclass(targetObject.getClass());
// 動態生成位元組碼並返回代理物件
return enhancer.create();
}
// 攔截方法
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 被織入的橫切內容,開始時間 before
System.out.println("使用cglib動態切入前=====");
// 呼叫方法
Object result = methodProxy.invoke(targetObject, args);
// 被織入的橫切內容,結束時間
System.out.println("使用cglib動態切入後=====");
return result;
}
public static void main(String[] args) {
Calculate ic = (Calculate) new CglibDynamicCalculate().getProxyObject(new Calculate());
ic.plus(10, 2);
ic.minus(20, 12);
}
}
下面是控制檯顯示的結果
使用cglib動態切入前=====
12
使用cglib動態切入後=====
使用cglib動態切入前=====
8
使用cglib動態切入後=====
未完成,待續