java 中 JDK 代理模式和 GGLIK 代理模式分别有什么不同呢?
目的
两种代理方式都是为了使得方法的调用具有可拓展性,易拓展型,而使用了不同的方式实现了大致相同的需求,在要求方面也有所不同
代码逻辑
现在有一个类Methods,继承于接口IMethods,我们需要拓展的就是这个类中的方法
add 和 mul,接口和类代码如下。
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());