代理模式实现

最近在看java的反射,看着看着就看到了jdk中动态代理的实现,于是将代理的知识点在这里介绍一下。在设计模式中有一种模式就是代理模式,它的主要作用是创建一个对象的代理,来控制对该对象的访问。本章节不深入讨论代理设计模式,而是介绍其两种实现方式:静态代理、动态代理。

简介

对于代理模式,我们首先要明白其中包含的角色:

  • 抽象角色:一般为接口,定义需要实现的业务方法。
  • 具体角色:接口的具体实现类,定义了业务的具体实现。
  • 代理角色:内部持有具体角色,通过具体角色实现业务方法,同时可以附加自己的操作。

静态代理

静态代理中静态是指,一个代理角色,在运行之前已经确定代理角色代理的具体类。如果想代理另一个具体角色需要重新实现一个代理类。
静态代理的UML图如下:

下面结合具体代码看一下静态代理的实现:
抽象角色:

public interface Service {
    String sayHello();
}

具体角色:

public class TVService implements Service {
    @Override
    public String sayHello() {
        return "tv service say hello!";
    }
}

代理角色:

public class ProxyService implements Service {
    private Service service;

    ProxyService(Service service) {
        this.service = service;
    }
    @Override
    public String sayHello() {
        return service.sayHello();
    }
}

测试代码:

public class StaticProxyTest {
    public static void main(String[] args) {
        TVService tvService = new TVService();
        ProxyService service = new ProxyService(tvService);
        String s = service.sayHello();
        System.out.println(s);
    }
}

从上面的代码可以看出,实现静态代理还是相对简单的。

动态代理

动态代理,顾名思义就是可以动态创建代理的对象。主要是通过实现InvocationHandler接口实现的。其内部是通过反射机制实现的。
产生动态代理的类如下:

public class MyInvocationHandler implements InvocationHandler {
    private Object obj;

    public Object bind(Object obj){
        this.obj = obj;
        return Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // invoke the method by reflect mechanism in java
        return method.invoke(this.obj,args);
    }
}

测试代码如下:

public class DynamicProxyTest {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); // 可以保存动态生成的class文件
        TVService tvService = new TVService();
        MyInvocationHandler handler = new MyInvocationHandler();
        // create proxy by reflect in java
        Service service = (Service) handler.bind(tvService);
        String s = service.sayHello();
        System.out.println(s);
    }
}

从上面代码可以看出,只要给handler.bind不同的类,就可以实现不同类的代理。对于被代理的类,要求必须实现某个接口(任何一个接口都可以)。因为在动态生成代理类时需要实现这个接口。
下面介绍一下的内部实现,其实重点就是Proxy.newProxyInstance返回的对象为什么就是动态代理了?我们分析一下其内部实现。
他传入的参数为:

  • 具体角色的类加载器
  • 具体角色实现的接口
  • InvocationHandler实现类。
    其中最重要的是如下两行代码。
Class<?> cl = getProxyClass0(loader, intfs);
return cons.newInstance(new Object[]{h});

第一行代码是获得一个动态生成的代理类,第二行是通过动态生成的代理类的构造函数创建一个代理对象。第二行就是一个简单反射使用。对于第一行就没有这么简单了。
在Proxy类中有一个proxyClassCache类,他缓存着创建的代理类,如果根据加载类和实现的接口没有在缓存中找到的话,就拼接出来一个字符串类,再将类动态编译加载到内存中。实现动态生成代理。第二行代码中构造函数中传入了一个InvocationHandler实现类,我们看一下动态拼接出来的类。


动态生成的类有如下特点:

  • 1,实现了传进来的接口Service。
  • 2,生成的方法,除了构造函数都是final类型的,限制继承实现。
  • 3,构造函数中传入了InvocationHandler作为参数。
  • 4,所有的方法都是通过反射的方法获得的。
  • 5,所有的方法在实现时都调用了InvocationHandler的invoke方法。

这就是为什么我们在调用生成的动态代理方法时总会调用到InvocationHandler实现类中的invoke方法了。

动态代理和静态代理比较

在需要代理很多类时,静态代理要实现很多代理类,但是动态代理类只需要实现InvocationHandler就可以实现多个类的代理。

posted @ 2018-04-13 18:13  arax  阅读(216)  评论(0编辑  收藏  举报