java動態代理模式(jdk和cglib)

爲什麼需要代理

假設有一個計算器類,類中定義了加,減,乘,除操作,然後我希望在每個方法執行前後打印一些提示信息,比如 “XX方法開始執行。。”,“XX方法結束執行。。”,要做到這一點,普通的解決方式是在每個方法前後加上 System.out.println("XX方法正在執行。。"),System.out.println("XX方法結束執行。。"),這樣寫起來十分麻煩,如果我們以後要擴充這個類,要加上乘方,開方等方法,還要再加上提示信息,代碼及其冗餘,也不便於維護。

代理模式可以解決這種囧境,利用這種模式,可以將一些通用的操作抽取出來,比如記錄日誌,權限管理等等,這些操作在很多地方都能用到,將這些操作單獨拿出來,運行時動態加進去,這時我們希望的。

代理分爲靜態代理:主要通過繼承和組合的方式實現,以及下面主要介紹的動態代理。

此外代理模式是Spring框架中AOP的基礎,所以理解代理模式非常重要。

使用jdk自帶的庫來實現動態代理

被代理的類需要實現特定的接口,我們先定義這個接口:

package com.hunan.proxy;

public interface Moveable {
	//move方法,參數爲移動速度,返回移動的總路程
	public double  move(double speed);
}

接下來我們寫一個Handler爲所有實現這個接口的類做代理,上面的接口中聲明瞭move方法,我們希望再執行move方法前後記錄日誌,利用代理來將日誌處理模塊提取出來:

package com.hunan.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LogHandler implements InvocationHandler {
	private Object target;
	
	public LogHandler(Object target) {
		super();
		this.target = target;
	}

	/*
	 * 參數說明:
	 * proxy 被代理的對象
	 * method 被代理對象的方法
	 * args 方法的參數
	 * 
	 * 返回值:
	 * Object  被代理的對象的方法的返回值
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 本例僅爲演示動態代理,具體的日誌處理過程就不具體實現了,僅打印一條提示信息。
		System.out.println("開始記錄日誌。。。");
		Object obj =  method.invoke(target, args);
		System.out.println("日誌記錄完畢。。。");
		return obj;
	}

}

注意:代理是代理某個特定接口的實現類,而不是代理某個特定類,所以儘量用Object引用指向對象,使代碼有更好的通用性。

第三步實現接口,用線程休眠的方式模擬汽車行駛:

package com.hunan.proxy;

import java.util.Random;

public class Car implements Moveable {

	@Override
	public double move(double speed) {
		System.out.println("汽車行駛中。。。");
		int moveTime =  new Random().nextInt(3000);
		try {
			Thread.sleep(moveTime);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return speed*moveTime;
	}

}

最後寫個main方法測試一下:

package com.hunan.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Main {

	public static void main(String[] args) {
		Car car = new Car();
		InvocationHandler h = new LogHandler(car);
		Class<?> cls = car.getClass();
		
		/*
		 * loader:類加載器
		 * interface:實現的接口
		 * h:InvocationHandler實例
		 */
		 Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);
		double distance = m.move(50);
		System.out.println("move:  " +  distance + " meters....");
	}

}

運行結果如下:


基於cglib的動態代理

使用cglib來實現動態代理需要引入cglib的依賴包,這個網上很容易下載到。cglib的代理實現方式實際上是基於繼承來實現的,它會創建一個被代理類的子類,在子類中添加日誌處理等模塊並調用父類的方法。這些子類由cglib幫我們管理。

仍然是代理前面的Moveable接口的實現類:

首先要實現MethodInterceptor接口:

package com.hunan.cglibproxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {
	
	private Enhancer enhancer = new Enhancer();
	
	public Object getProxy(Class<?> clazz) {
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		
		return enhancer.create();
	}
	
	/*
	 * 攔截所有目標類方法的調用
	 * obj:目標類的實例
	 * m: 目標方法的反射對象
	 * args:方法的參數
	 * proxy:代理類的實例
	 * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
	 */
	@Override
	public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("日誌開始。。。");
		
		//代理類調用父類方法
		Object distance= proxy.invokeSuper(obj, args);
		
		System.out.println("日誌結束。。。");
		return distance;
	}

}

這個enhancer就是用於實現代理的子類了,不過cglib幫我們包裝了一下。

接下來寫一個Train類實現Moveable接口:

package com.hunan.cglibproxy;

import java.util.Random;

import com.hunan.proxy.Moveable;

public class Train implements Moveable {

	@Override
	public double move(double speed) {
		System.out.println("火車行駛中。。。");
		int moveTime =  new Random().nextInt(3000);
		try {
			Thread.sleep(moveTime);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return speed*moveTime;
	}

}

寫一個main方法測試一下:

package com.hunan.cglibproxy;

public class Client {

	public static void main(String[] args) {
		CglibProxy proxy = new CglibProxy();
		Train t = (Train)proxy.getProxy(Train.class);
		double distance = t.move(100);
		System.out.println("火車行駛了: " + distance + "路程。。");
	}

}

打印輸出如下:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章