zl程序教程

您现在的位置是:首页 >  云平台

当前栏目

设计模式--代理模式(Proxy)

2023-09-11 14:16:24 时间

1. 代理模式

1.1. 概念

代理是一种结构设计模式,它提供一个对象作为客户端使用的实际对象的替代品。代理接收客户端请求,完成一些工作(访问控制、缓存等),然后将请求传递给服务对象。

代理对象具有与实际对象拥有相同的接口,这使得它在传递给客户端时可以与实际对象互换。

使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能

1.2. 结构

  • Subject:抽象类或接口,是一个最普通的业务类型定义,无特殊要求。
  • RealSubject:被委托角色、被代理角色。是业务逻辑的具体执行者。
  • Proxy:委托类、代理类。它把所有抽象类或接口定义的方法给RealSubject实现,并且在RealSubject处理完毕前后做预处理和善后工作。(最简单的比如打印日志)
    在这里插入图片描述

1.3. 使用场景

使用场景包括但不限于如下:

  • 远程代理 :为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。
  • 虚拟代理:通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销。
  • 缓冲代理:为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间。
  • 保护代理:可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限。
  • 智能引用:要为一个对象的访问(引用)提供一些额外的操作时可以使用

1.4. 分类

  • 静态代理: 静态代理是指预先确定了代理与被代理者的关系. 代理类与被代理类的依赖关系在编译期间就确定了
  • 动态代理: 代理类与被代理类的依赖关系是在程序运行期间确定的,实现方案有2种: JAVA动态代理和cglib动态代理

1.5. cgLib的动态代理原理

动态生成一个要代理类的子类,子类重写要代理类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB缺点:对于final方法 或 final类(如StringBuffer, String等),无法进行代理

2. 代码示例

假设要记录对象各个方法的执行时间,则通过动态代理实现如下:

2.1. JDK动态代理

class DynamicProxyTest {

    public static void main(String[] args) throws Exception {
        Map mapProxyInstance = (Map) Proxy.newProxyInstance(
                DynamicProxyTest.class.getClassLoader(), new Class[] { Map.class },
                new TimingDynamicInvocationHandler(new HashMap<>()));

        mapProxyInstance.put("hello", "world");
        mapProxyInstance.get("hello");

        CharSequence csProxyInstance = (CharSequence) Proxy.newProxyInstance(
                DynamicProxyTest.class.getClassLoader(),
                new Class[] { CharSequence.class },
                new TimingDynamicInvocationHandler("Hello World"));
        csProxyInstance.charAt(10);
        csProxyInstance.length();
    }
}

class TimingDynamicInvocationHandler implements InvocationHandler {

    private static Logger LOGGER = LoggerFactory.getLogger(TimingDynamicInvocationHandler.class);

    private final Map<String, Method> methods = new HashMap<>();

    private Object target;

    public TimingDynamicInvocationHandler(Object target) {
        this.target = target;

        for(Method method: target.getClass().getDeclaredMethods()) {
            this.methods.put(method.getName(), method);
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.nanoTime();
        Object result = methods.get(method.getName()).invoke(target, args);
        long elapsed = System.nanoTime() - start;

        LOGGER.info("Executing {} finished in {} ns", method.getName(), elapsed);

        return result;
    }
}

2.2. cglib动态代理

class DynamicProxyTest {

    public static void main(String[] args) throws Exception {
        Map<String,String> mapProxyInstance = (Map) new TimingCglibProxy(new HashMap<String,String>()).getProxyInstance();
        mapProxyInstance.put("hello", "world");
        mapProxyInstance.get("hello");

        //报错,Cannot subclass final class java.lang.StringBuffer
        StringBuffer sbProxyInstance = (StringBuffer) new TimingCglibProxy(new StringBuffer("hello world")).getProxyInstance();
        sbProxyInstance.length();
    }
}


class TimingCglibProxy implements MethodInterceptor {

    private static Logger LOGGER = LoggerFactory.getLogger(TimingCglibProxy.class);

    private final Map<String, Method> methods = new HashMap<>();

    private Object target;

    public TimingCglibProxy(Object target) {
        this.target = target;

        for (Method method : target.getClass().getDeclaredMethods()) {
            this.methods.put(method.getName(), method);
        }
    }
    public Object getProxyInstance() {
        //工具类
        Enhancer en = new Enhancer();
        //设置父类
        en.setSuperclass(target.getClass());
        //设置回调函数
        en.setCallback(this);
        //创建子类代理对象
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long start = System.nanoTime();
        Object result = methods.get(method.getName()).invoke(target, objects);
        long elapsed = System.nanoTime() - start;
        LOGGER.info("Executing {} finished in {} ns", method.getName(), elapsed);
        return result;
    }
}