Java中兩種動態代理JDK和GGLIK的比較

java 中 JDK 代理模式和 GGLIK 代理模式分別有什麼不同呢?

目的


兩種代理方式都是爲了使得方法的調用具有可拓展性,易拓展型,而使用了不同的方式實現了大致相同的需求,在要求方面也有所不同


代碼邏輯

現在有一個類Methods,繼承於接口IMethods,我們需要拓展的就是這個類中的方法
addmul,接口和類代碼如下。

public interface IMethods {

	public int add(int x,int y);
	
	public int mul(int x,int y);
	
}

public class Methods implements IMethods {

	@Override
	public int add(int x,int y) {
		System.out.println("函數結果爲:"+(x+y));
		return x+y;
	}
	
	@Override
	public int mul(int x,int y) {
		System.out.println("函數結果爲:"+(x*y));
		return x*y;
	}

通過兩種方式來一更簡便地拓展這個類中地的方法內容。


JDK代理模式

自己創建的封裝好的JDK代理工具類:

public class JdkProxyFactory {
	
	static Object object = null;

	static public Object get(Object object) throws InstantiationException, IllegalAccessException {
		JdkProxyFactory.object = object.getClass().newInstance();
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),new  InvocationHandler() {
			
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
				System.out.println(method.getName()+" before!!!!!!!!!!!!!!!!!!!!!!!!");		
				int re = (int) method.invoke(object, args);
				System.out.println(method.getName()+" after!!!!!!!!!!!!!!!!!!!!!!!!");			
				return re;
			}
		});
	}
	
}

在測試類中調用:

import java.lang.reflect.Proxy;

public class Test {

	public static void main(String[] args) throws InstantiationException, IllegalAccessException {
		
		IMethods methods = (IMethods) JdkProxyFactory.get(new Methods());
		int re = methods.add(1, 0);
		re = methods.mul(1, 0);		
		
	}
	
}

運行結果:

add before!!!!!!!!!!!!!!!!!!!!!!!!
函數結果爲:1
add after!!!!!!!!!!!!!!!!!!!!!!!!
mul before!!!!!!!!!!!!!!!!!!!!!!!!
函數結果爲:0
mul after!!!!!!!!!!!!!!!!!!!!!!!!

代碼邏輯:

JdkProxyFactory 封裝類的 get 函數:static public Object get(Object object)
想要拓展一個已知的類中的方法,只要獲取到這個類的對象,就足以封裝好一個拓展的方法。

JdkProxyFactory.object = object.getClass().newInstance();

將獲取到的對象通過反射創建一個新的對象,兩個對象屬於一個Class,賦值給類中屬性object 。

 return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),new  InvocationHandler(){})

通過java.lang.reflect中的Proxy,也就是代理類,調用其靜態方法產生一個代理類對象,並傳出函數,三個參數分別是類加載方式,實現的接口數組,接口InvocationHandler的實現對象。通過這三個參數即可以產生一個動態代理類,類加載方式並不重要,其實現的接口數組和InvocationHandler的實現對象纔是如何形成一個代理類的關鍵。

通過反編譯方式找到這個動態代理類的代碼。

public final class $Proxy0 extends Proxy
    implements IMethods 

通過類名即看到了重要的信息:這個類implements於IMethods接口,繼承於Proxy類。也就是需要我們進行代理的類Methods所implements的接口。接口的特性是什麼,其實現類均具有其抽象方法,也就是說,產生的抽象方法從方法數量和名字上來說已經達到了我們的需求。那麼它又是如何對原來的方法中的內容進行拓展的呢?

public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

這個是類中的屬性,是不是很熟悉,就是我們在創建動態代理類時傳入的參數InvocationHandler對象,這個屬性有大用處!!!

 public final int add(int i, int j)
    {
        try
        {
            return ((Integer)super.h.invoke(this, m3, new Object[] {
                Integer.valueOf(i), Integer.valueOf(j)
            })).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

終於找到了方法中的代碼。

return ((Integer)super.h.invoke(this, m3, new Object[] {
                Integer.valueOf(i), Integer.valueOf(j)
            })).intValue();

通過一堆亂七八糟的調用大概意思就是調用父類中屬性InvocationHandler中的invoke方法,前面說了,它繼承於Proxy,還有前面的super(invocationhandler);繞了個彎,這個InvocationHandler還是創建動態代理類傳進來的參數啊!!!知道爲啥前面說它很重要了吧。也就是說,到現在位置,方法的執行過程在於我們傳進來的InvocationHandler中的invoke方法。

再找到我們傳入參數時實現的這個invoke方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
	System.out.println(method.getName()+" before!!!!!!!!!!!!!!!!!!!!!!!!");		
	int re = (int) method.invoke(object, args);
	System.out.println(method.getName()+" after!!!!!!!!!!!!!!!!!!!!!!!!");			
	return re;
}

前面和後面都是我們拓展的輸出語句,而最重要的是調用原方法中的內容:

int re = (int) method.invoke(object, args);

使用反射通過從動態代理類傳過來的method去調用object對象中的這個方法,後面的參數是方法的參數,這個object對象就是前面JdkProxyFactory.object = object.getClass().newInstance();傳遞過來的需要我們進行動態代理的Methods類對象,以此來執行原方法中的內容。


GGLIK代理模式

自己創建的封裝好的GGLIK代理工具類:

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import t1.Computer;

public class CGLibProxyFactory {
	
	public static Callback callback = null;

	public static Object get(Object object) {
		
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(object.getClass());
		callback = new MethodInterceptor() {
			
			@Override
			public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
				System.out.println(arg1.getName()+" before!!!!!!!!!!!!!!!!!!!!!!!!");
				Object re = arg3.invoke(arg0, arg2);
				System.out.println(arg1.getName()+" after!!!!!!!!!!!!!!!!!!!!!!!!");	
				return re;
			}
		};
		
		enhancer.setCallback(callback);
		return enhancer.create();
	}
	
}

在測試類中調用:

public class Test {

	public static void main(String[] args) throws InstantiationException, IllegalAccessException {
		
		Methods methods2 = (Methods) CGLibProxyFactory.get(new Methods());
		int re2 = methods.add(1, 0);
		re2 = methods.mul(1, 0);
		
	}
	
}

運行結果:

add before!!!!!!!!!!!!!!!!!!!!!!!!
函數結果爲:1
add after!!!!!!!!!!!!!!!!!!!!!!!!
mul before!!!!!!!!!!!!!!!!!!!!!!!!
函數結果爲:0
mul after!!!!!!!!!!!!!!!!!!!!!!!!

代碼邏輯

兩種方式的實現細節方面很是相似,我們只通過大致的邏輯來對比:

enhancer.setSuperclass(object.getClass());

這裏傳進來的直接就是Methods對象,而不是它實現的接口,這裏我們在後面比較的時候詳細說。

@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
	System.out.println(arg1.getName()+" before!!!!!!!!!!!!!!!!!!!!!!!!");
	Object re = arg3.invoke(arg0, arg2);
	System.out.println(arg1.getName()+" after!!!!!!!!!!!!!!!!!!!!!!!!");	
	return re;
}

通過arg3.invoke(arg0, arg2);來還原原方法中的代碼,與上面類似。


比較

1.實現邏輯

JDK是通過動態生成實現目標類實現的接口數組的類,有點繞口,也就是說新的類和目標類實現的接口都一樣,但兩個類直接並沒有直接的關係,新的類繼承自Proxy。

GGLIK是通過生成目標類的子類,子類擁有父類的所有方法,通過這種方式實現所需的功能。

2.使用要求

JDK動態代理方式的實現要求更高,更加複雜,由於它是通過接口來實現方法的拓展,因此如果目標類沒有繼承任何接口,將無法使用這種方式。且即使目標類繼承了接口,如果自身仍有接口中沒有的方法,那麼這些方法也無法拓展,相反GGLIK就沒有這麼多的要求了。

3.獲取形式

從上面兩個類之間的關係,在獲取這些代理類對象的時候要尤其注意,並不能隨意的轉型。

首先兩種方法不管是實現目標類實現的接口還是目標類的子類,總之,它們都實現了接口,因此,用接口類型獲取這些對象是毫無問題的。
而如果用目標類獲取動態類對象的時候,就有問題了,JDK動態代理方式獲取的類與目標類同級,因此不能用目標類獲取動態類。

	IMethods methods = (IMethods) JdkProxyFactory.get(new Methods());
	IMethods methods2 = (IMethods) CGLibProxyFactory.get(new Methods());
//	Methods methods3 = (Methods) JdkProxyFactory.get(new Methods());		//錯誤	
	Methods methods4 = (Methods) CGLibProxyFactory.get(new Methods());
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章