阴阳大论之代理模式

阴阳大论之代理模式

目录

代理模式


定义

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

优缺点

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

使用场景

  • 远程代理

为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是本电脑中,也可以在另一台电脑中。最典型的例子就是——客户端调用Web服务或WCF服务。

  • 虚拟代理(Virtual PRoxy)会

推迟真正所需对象实例化时间. 在需要真正的对象工作之前, 如果代理对象能够处理, 那么暂时不需要真正对象来出手.
优点: 在应用程序启动时,由于不需要创建和装载所有的对象,因此加速了应用程序的启动。

  • Copy-on-Write 代理

虚拟代理的一种,把复制(或者叫克隆)拖延到只有在客户端需要时,才真正采取行动。

  • 保护(Protect or Access)代理

控制一个对象的访问,可以给不同的用户提供不同级别的使用权限。

  • Cache代理

为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以这些结果。

  • 防火墙(Firewall)代理

保护目标不让恶意用户接近

  • 同步化(Synchronization)代理

使几个用户能够同时使用一个对象而没有冲突。

  • 智能引用(Smart Reference)代理

当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等

与其它模式的区别

  • 和适配器模式的区别

适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。

  • 和装饰器模式的区别

装饰器模式为了增强功能,而代理模式是为了加以控制。

静态代理

JDK实现动态代理

实现流程

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

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反射来实现。

  1. 创建代理类的源码;

拿到被代理类实现的接口类对象,遍历里面的方法,以字符串的形式拼凑出代理类源码(动态代理类与被代理类实现同一接口在此体现),将代理类的源码写到本地java文件

  1. 将源码进行编译成字节码;

读取源码,编译java文件,得到.class字节码文件(的路径)

  1. 将字节码加载到内存;
  2. 实例化代理类对象并返回给调用者。

CGLIB实现动态代理

定义

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

实现流程

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

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();
            }
        }
    });
原文地址:https://www.cnblogs.com/shiyusen/p/10515466.html