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());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值