您的位置:首页 > 编程语言 > Java开发

Java动态代理解析

2016-10-31 20:06 239 查看
首先简单介绍代理模式,接着详解Java中动态代理的使用并分析其运行过程。

代理模式具体理论可以参照设计模式类书籍等。简单假定为请求者向某个实体请求方法,在实现上可以分以下两种类别:一是代理类中拥有实体的对象,方法中通过该对象返回请求想要的结果;二是代理类继承实体类的接口,进而去实现实体类的接口,同时也需要拥有实体类对象,在接口实现中仍然是调用实体类对象的方法。

第二种实现在设计模式的书上一般被普遍认为是代理模式的原型,这种实现更为规范,易于扩展。下面给出结构图:



在Proxy类中拥有RealSubject的对象,故在Proxy中的Request方法中调用RealSubject的Request方法即可。

下面介绍Java中动态代理的使用。

Java中动态代理和上图类似,拥有proxy的自动实现,即能够去自动调用RealSubject的Request方法,并提供了接口,让我们通过实现该接口,去增强Request方法。例如增加Request方法调用前和调用后的操作。这个特点的优势非常明显,可以在不改变Request方法具体内容的情况下补充代码。这种实现符合代码的封闭开放原则,在很多地方得到应用,如Spring中的AOP,明显的减少了不愿写在一起的代码的耦合度。

Java中需先定义实体类的接口,并用类实现接口。相当于上图中的Subject和RealSubject。Java动态代理中提供了接口InvocationHandler,我们需要将代理方法的增强放入这个接口实现类的invoke方法中。随后我们并不需要实现Proxy的相关内容,仅需要直接调用Proxy类中的newProxyInstance方法即可创建代理的对象。接着就可以使用该对象调用方法。下面给出非常简单的代码实现,仅用于快速理解:

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

public class app {

public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ClassLoader load = realSubject.getClass().getClassLoader();
Class<?>[] interfaces = realSubject.getClass().getInterfaces();
HandlerImpl handler = new HandlerImpl(realSubject);
Object proxy = Proxy.newProxyInstance(load, interfaces, handler);
Subject s = (Subject)proxy;
s.Request();
}

}

interface Subject {
void Request();
}

class RealSubject implements Subject {

@Override
public void Request() {
// TODO Auto-generated method stub
System.out.println("This is RealSubject's Request()");
}

}

class HandlerImpl implements InvocationHandler{

private Object realSubject;

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("before");
method.invoke(realSubject, args);
System.out.println("after");
return null;
}

public HandlerImpl(Object realSubject){
this.realSubject = realSubject;
}
}
上述程序的运行结果是:

before

This is RealSubject's Request()

after

通过在类HandlerImpl 中invoke的实现,我们将增强的代码放入before和after处即可完成我们的目的。

通过以上的简单实现我们可以看出Java动态代理屏蔽了Proxy的实现,通过method.invoke(realSubject, args)这句调用我们基本可以看出动态代理的实现并不复杂,传入的Method即为RealSubject中的Requset方法。下面我们介绍下运行的原理。
首先给出HandlerImpl 和Proxy的结构图:



InvocationHandler接口和Proxy的代码我们可以直接从源代码中看到,唯一对我们屏蔽的即位ProxySubject实现类。可以通过反编译class文件查看其内容。
在我们的代码中,经历了以下几个过程:
(1)HandlerImpl handler = new HandlerImpl(realSubject); 易于理解,创建了handler,并将HanderImpl中的realSubject赋值。
(2)Object proxy = Proxy.newProxyInstance(load, interfacecar, handler); 这里是理解的关键。首先Proxy会给自己的InvocationHandler对象h进行赋值,随后由于传进了interfaces数组。可以直接获取interfaces的名字。由于传进了ClassLoader的对象Load(这里的Load是RealSubject的Load)。故我们可以通过名字从该Load中获取Method对象,即获得Request()方法的对象。
语句很简单,这样写就可以了,method_i = Class.forName("RealSubject").getMethod("Request", loader.loadClass("RealSubject"));这里是自己的理解,动态代理中的具体实现估计大同小异,总之是通过传入的ClassLoader对象和interfaces数组对象获取方法的名字然后获取该方法的对象。

    通过以上操作,我们创建了ProxySubject的类,并拥有了Method对象。即为RealSubject中Request方法的对象。

    (3)s.Request()后; 这里ProxySubject类直接调用h.invoke()即可,对象h是从父类Proxy继承而来,其实从看Proxy源码时h的属性为保护即可看出一定是通过子类的调用实现。传入参数分别为this,method_i,和方法的参数。本例中方法无参数,传入null即可。

通过以上介绍我们大体介绍了Java动态代理的使用和实现机制,后面有机会会尝试自己写一个类去继承Proxy实现类似ProxySubject的功能。下次有机会整理下CGLIB中代理的机制
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java