zoukankan      html  css  js  c++  java
  • 代理模式实现

    最近在看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就可以实现多个类的代理。

    I am chris, and what about you?
  • 相关阅读:
    UVALive 7752 Free Figurines (瞎搞)
    ifram的使用 左边是<a>链接 右边是对应网页嵌套的显示网页链接内容 和toggle的收放用法
    java.util.Collections.synchronizedSet()方法的使用
    hibernate的反向生成改懒加载的地方
    SSM的XML和WEB.XML的配置
    通过System获取java环境变量的路径
    Java:对象的强、软、弱和虚引用的区别
    Struts2方法调用的三种方式(有新的!调用方法的说明)
    静态代理,动态代理,Cglib代理详解
    spring自定义注解拦截器的配置
  • 原文地址:https://www.cnblogs.com/arax/p/8822540.html
Copyright © 2011-2022 走看看