Java动态代理

来源:互联网 发布:科学 知乎 编辑:程序博客网 时间:2024/06/10 00:35

1. 代理

在某些情况下,我们不希望,或者是不能直接访问对象A,而是通过访问一个中介对象B,由B去访问A达成目的,这种方式我们就称为代理。对象A所属类我们称为委托类,也被称为代理类,对象B所属类称为代理类。

代理优点有:

  • 隐藏委托类的实现
  • 解耦,不改变委托类代码情况下做一些额外处理,比如添加初始判断及其他公共操作

2. 静态代理

代理类在程序运行前已经存在的代理方式称为静态代理。下面是一个简单的静态代理实例:

class ClassA {    public void operateMethod1() {};    public void operateMethod2() {};    public void operateMethod3() {};}public class ClassB {    private ClassA a;    public ClassB(ClassA a) {        this.a = a;    }    public void operateMethod1() {        a.operateMethod1();    };    public void operateMethod2() {        a.operateMethod2();    };    // not export operateMethod3()}


上面ClassA是委托类,ClassB是代理类,ClassB中的函数都是直接调用ClassA相应的函数,并且隐藏了ClassA的operateMethod3()方法。

静态代理中代理类和委托类也常常继承同一父类或实现同一接口。


3. 动态代理
定义:代理类在程序运行前不存在、运行时由程序动态生成的代理方式称为动态代理。

Java提供了动态代理的实现方式,可以在运行时刻动态生成代理类。这种代理方式的一大好处是可以方便对代理类的方法做统一或特殊处理,如记录所有方法执行时间、所有方法执行前添加验证判断、对某个特殊函数进行特殊操作,而不用像静态代理方式那样需要修改每个函数。


实现动态代理包括三步:

(1) 新建委托类

(2) 实现InvocationHandler接口,这是负责连接代理类和委托类的中间类必须实现的接口

(3) 通过Proxy类新建代理类对象


下面通过实例具体介绍。假如现在我们想要统计某个类所有方法的执行时间,传统的方式是在类的每个函数前打点统计,动态代理方式则是这样的:

3.1 新建委托类

public interface Operate {    public void operateMethod1();    public void operateMethod2();    public void operateMethod3();}public class OperateImpl implements Operate {    @Override    public void operateMethod1() {        System.out.println("Invoke OperateMethod1");        sleep(100);    }    @Override    public void operateMethod2() {        System.out.println("Invoke OperateMethod1");        sleep(200);    }    @Override    public void operateMethod3() {        System.out.println("Invoke OperateMethod1");        sleep(300);    }    private void sleep(long millSeconds) {        try {            Thread.sleep(millSeconds);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

Operate是一个接口,定义了一些函数,现在我们要统计这些函数的执行时间。

OperateImpl是委托类,实现Operate接口。每个方法简单输出字符串,并等待一段时间。

动态代理要求委托类必须实现某个接口,比如这里委托类OperateImpl实现了Operate。


3.2 实现InvocationHandler接口

public class TimingInvocationHandler implements InvocationHandler {    private Object target;    public TimingInvocationHandler() {    }    public TimingInvocationHandler(Object target) {        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        long start = System.currentTimeMillis();        Object obj = method.invoke(target, args);        System.out.println(method.getName() + " costs time is: "                           + (System.currentTimeMillis() - start));        return obj;    }}

target属性表示委托类对象

InvocationHandler是负责连接代理类和委托类的中间类必须实现的接口,该接口只有一个方法:

public Object invoke(Object proxy, Method method, Object[] args)

参数:

  • proxy:通过Proxy.newProxyInstance()生成的代理类对象
  • method:表示代理对象被调用的函数
  • args:表示代理对象被调用的函数的参数


调用代理对象的每个函数实际最终都是调用InvocationHandler的invoke方法。这里我们在invoke实现中添加了开始结束计时,其中还调用了委托类对象target的相应方法,这样便完成了统计执行时间的需求。

此外,在invoke函数中,我们也可以通过对method做一些判断,从而对某些函数特殊处理。


3.3 通过Proxy类静态函数生成代理类的对象

public class ProxyTest {    public static void main(String[] args) {        TimingInvocationHandler timingInvocationHandler = new TimingInvocationHandler(            new OperateImpl());        //创建代理类的实例对象        Operate operate = (Operate) (Proxy.newProxyInstance(Operate.class.getClassLoader(),            new Class[] { Operate.class }, timingInvocationHandler));        //调用代理类的方法        operate.operateMethod1();        System.out.println();        operate.operateMethod2();        System.out.println();        operate.operateMethod3();    }}

这里我们先将委托类对象new OperateImpl()作为TimingInvocationHandler构造方法入参创建timeingInvocationHandler对象;

然后通过Proxy.newProxyInstance()方法新建一个代理对象,实际代理类就是在这时候动态生成的。我们调用该代理对象的方法就会调用到timeingInvocationHandler的invoke方法,而invoke方法实现了调用委托类对象new OperateImpl()相应的method。


public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

loader:表示类加载器

interfaces:表示委托类的接口,生成代理类时需要实现这些接口

h:表示InvocationHandler实现类对象,负责连接代理类和委托类的中间类


可以这样理解动态代理:如上的动态代理实现实际是双层的静态代理,开发者提供了委托类B,程序动态生成了代理类A,开发者还需要提供一个实现了InvocationHandler接口的类C,C连接代理类A和委托类B,它是代理类A的委托类,委托类B的代理类。用户直接调用代理类A的对象,A将调用转发给委托类C,委托类C再将调用转发给它的委托类B。


0 0
原创粉丝点击