1. 程式人生 > >通過例子全面解析Java中的Aop技術-包括靜態代理,動態代理,Spring Aop(全面解析,附帶全部原始碼,小白看一遍也可以理解)

通過例子全面解析Java中的Aop技術-包括靜態代理,動態代理,Spring Aop(全面解析,附帶全部原始碼,小白看一遍也可以理解)

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動態切入後=====

未完成,待續