专栏首页Java学习录阴阳大轮之代理模式

阴阳大轮之代理模式

定义

在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层

优缺点

  • 职责清晰
  • 高扩展性
  • 智能化
  • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
  • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂

使用场景

  • 远程代理 为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是本电脑中,也可以在另一台电脑中。最典型的例子就是——客户端调用Web服务或WCF服务。
  • 虚拟代理(Virtual PRoxy)会 推迟真正所需对象实例化时间. 在需要真正的对象工作之前, 如果代理对象能够处理, 那么暂时不需要真正对象来出手. 优点: 在应用程序启动时,由于不需要创建和装载所有的对象,因此加速了应用程序的启动。
  • Copy-on-Write 代理 虚拟代理的一种,把复制(或者叫克隆)拖延到只有在客户端需要时,才真正采取行动。
  • 保护(Protect or Access)代理 控制一个对象的访问,可以给不同的用户提供不同级别的使用权限。
  • Cache代理 为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以这些结果。
  • 防火墙(Firewall)代理 保护目标不让恶意用户接近
  • 同步化(Synchronization)代理 使几个用户能够同时使用一个对象而没有冲突。
  • 智能引用(Smart Reference)代理 当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等

与其它模式的区别

  • 和适配器模式的区别 适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
  • 和装饰器模式的区别 装饰器模式为了增强功能,而代理模式是为了加以控制。

JDK实现动态代理

实现流程

  • 实现InvocationHandler接口,创建自己的调用处理器
  • 调用Proxy的静态方法,创建代理类并生成相应的代理对象
  • 源码demo
/**
 * 经纪人,动态代理
 *
 * @author 石玉森
 **/

public class JDKDynamicProxyFactory<T> implements InvocationHandler {

    /**
     * 委托类实例:被代理的真实对象实例
     */
    private T target;

    public JDKDynamicProxyFactory(T target) {
        this.target = target;
    }

    /**
     *
     * @param proxy    生成的代理类实例
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用被被代理的方法 before");
        Object result = method.invoke(target,args);
        System.out.println("调用被被代理的方法 after");
        return result;
    }

    /**
     * 创建代理类实例
     * @return
     */
    public T getProxyInstance(){
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class[] interfaces = target.getClass().getInterfaces();
        Object proxy = Proxy.newProxyInstance(classLoader,interfaces,this);
        return (T) proxy;
    }
}

实现原理

JDK动态代理是基于java反射来实现。

  • 创建代理类的源码; 拿到被代理类实现的接口类对象,遍历里面的方法,以字符串的形式拼凑出代理类源码(动态代理类与被代理类实现同一接口在此体现),将代理类的源码写到本地java文件
  • 将源码进行编译成字节码; 读取源码,编译java文件,得到.class字节码文件(的路径)
  • 将字节码加载到内存;
  • 实例化代理类对象并返回给调用者

CGLIB实现动态代理

定义

  • CGLIB是一个高性能的代码生成类库,被Spring广泛应用。其底层是通过ASM字节码框架生成类的字节码,达到动态创建类的目的。

实现流程

  • 实现MethodInterceptor接口,创建自己的调用处理器
  • 通过Enhancer类增强工具, 创建代理类并生成相应的代理对象
  • 源码demo
**
 * cglib动态代理实现
 *
 * @author 石玉森
 **/

public class CglibDynamicProxyFactory<T> implements MethodInterceptor {
    /**
     * 被代理类:委托类
     */
    private T target;
    /**
     * @param o           代理的对象(生成的被代理类的子类实例)
     * @param method      被代理的方法
     * @param objects     被代理的方法的参数
     * @param methodProxy 代理的对的方法(生成的被代理类的子类实例)
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        System.out.println("调用被被代理的方法 before");
//        result = method.invoke(o,objects);//不对
        result = method.invoke(this.target, objects);//可以
//        result = methodProxy.invoke(this.target, objects);//可以
//        result = methodProxy.invokeSuper(o, objects);//可以
        System.out.println("调用被被代理的方法 after");
        return result;
    }
    public T getProxyInstance(T target) {
        this.target=target;
        Enhancer enhancer = new Enhancer();
        //设置创建子类的类,即指定为哪个类产生代理类
        enhancer.setSuperclass(target.getClass());
        /*设置回调函数 setCallback设置被代理类的public非final方法被调用时的处理类
         * */
        enhancer.setCallback(this);
        //通过字节码技术动态创建子类实例
        return (T) enhancer.create();
    }
}

实现原理

  • 生成代理类的二进制字节码文件;
  • 加载二进制字节码,生成Class对象( 例如使用Class.forName()方法 );
  • 通过反射机制获得实例构造,并创建代理类对象

静态代理、CGLIB与JDK实现动态代理的区别

代理模式

优点

缺点

静态代理

简单

代码不能复用

JDK动态代理

动态代码生成快

执行慢,强制实现接口

CGLIB动态代理

生成代码慢

执行快,不需实现接口

代理模式在spring中的应用

spring AOP默认使用JDK动态代理实现。可以通过配置强制使用cglib代理。

CopyOnWriteArrayList类使用代理模式实现

/***
* 静态代理:实现List
* 随机存取:实现RandomAccess
* 克隆:实现Cloneable
*/
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    final transient ReentrantLock lock = new ReentrantLock();
    //委托类。初始化时即实例化,不像虚拟代理懒加载
    private transient volatile Object[] array;

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
}

代理模式在Struts2中的应用

struts2中的拦截器实现

代理模式在RPC中的应用

/**
* 服务生产者
* 服务生产者接受到消费方的调用,通过反射调起实际接口
*/
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
    String interfaceName = input.readUTF();
    String methodName = input.readUTF();
    Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
    Object[] arguments = (Object[]) input.readObject();
    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());

    try {
        if (!interfaceName.equals(interfaceClazz.getName())) {
            throw new IllegalAccessException("Interface wrong, export:" + interfaceClazz  + " refer:" + interfaceName);
        }
        Method method = service.getClass().getMethod(methodName, parameterTypes);
        Object result = method.invoke(service, arguments);
        output.writeObject(result);
    } catch (Throwable t) {
        output.writeObject(t);
    } finally {
        output.close();
    }
} finally {
    input.close();
}

/**
* 服务消费方
* 服务消费方在本地通过代理的模式实例化代理类,代理类内部通过socket调用远程生产者
*/
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass},
    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Socket socket =null;
            try {
                socket = new Socket(host, port);
                ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                try {
                    output.writeUTF(method.getName());
                    output.writeObject(method.getParameterTypes());
                    output.writeObject(args);
                    ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                    try {
                        Object result = input.readObject();
                        return result;
                    } finally {
                        input.close();
                    }
                } finally {
                    output.close();
                }
            } finally {
                socket.close();
            }
        }
    });

本文分享自微信公众号 - Java学习录(Javaxuexilu),作者:石玉森

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java中的代理模式

    在Java中我们最熟悉的使用场景就是SpringAOP,本篇文章即是SpringAOP源码分析的前置文章

    Java学习录
  • 浅谈Java类加载器

    注意最后的null值应该是启动类加载器、单因为是C++语言编写的,所以无法获取到相关的信息

    Java学习录
  • Linux常用命令速查-定时任务

    anacron是一个按天为单位周期性运行某些命令的工具,使用此工具需要指定任务的周期、延迟(分钟)、id、shell。

    Java学习录
  • 动态代理详解

    动态代理它可以直接给某一个目标对象生成一个代理对象,而不需要代理类存在。     动态代理与代理模式原理是一样的,只是它没有具体的代理类,直接通过反射生成了一...

    黑泽君
  • Java动态代理

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

    用户1205080
  • Java代理和动态代理机制分析和应用

    本博文中项目代码已开源下载地址:GitHub Java代理和动态代理机制分析和应用 概述 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个...

    CrazyCodeBoy
  • 设计模式之代理模式(Proxy Pattern)

    从UML图中,可以看出代理类与真正实现的类都是继承了抽象的主题类,这样的好处在于代理类可以与实际的类有相同的方法,可以保证客户端使用的透明性。

    一个会写诗的程序员
  • 正向代理和反向代理

    小伍哥通过一个例子来解释一下什么叫代理。 比如有个A和B可以直接交流,现在A做大了不会接触B了,来了一个C,然后B交流,然后把交流的内容再回复给A,这个叫做代理...

    奕仁
  • JDK动态代理

    这里是最简单的Java接口和实现类的关系,此时可以开始动态代理了,一般会分为两个步骤:第一是建立代理对象和真实服务对象的代理和被代理关系,第二步是实现代理对象具...

    itlemon
  • win10 UWP 动画 动画入门

    在 UWP 移动元素的动画,可以使用 RenderTransform 移动,然后使用动画修改 RenderTransform 进行动画。关于元素移动,请看 wi...

    林德熙

扫码关注云+社区

领取腾讯云代金券