• 设计模式学习(六):代理模式


    设计模式学习(六):代理模式

    作者:Grey

    原文地址:

    博客园:设计模式学习(六):代理模式

    CSDN:设计模式学习(六):代理模式

    代理模式

    代理模式是结构型模式,分为静态代理和动态代理。

    静态代理

    举个例子,假设需要在某个类的某段代码的前后加上日志记录,就可以通过静态代理的方式实现,代码如下

    public class Main {
        public static void main(String[] args) {
            new Tank().move();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    假设需要在move方法的前后都加上日志记录,我们可以设置一个代理类

    public class TankLogProxy implements Moveable {
        private Moveable m;
    
        public TankLogProxy(Moveable m) {
            this.m = m;
        }
    
        @Override
        public void move() {
            System.out.println("log before");
            m.move();
            System.out.println("log after");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    通过上述改造,原先的调用就改成了

    public class Main {
        public static void main(String[] args) {
            new TankLogProxy(new Tank()).move();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    即可实现在 move 方法调用前后加入日志记录的操作。

    UML图如下:

    image

    动态代理

    JDK 自带方式

    即实现InvocationHandler接口。

    还是以上例说明,如果需要通过 JDK 自带的方式来完成上述功能,可以这样来做

    public class MovableProxy implements InvocationHandler {
        private Movable movable;
    
        public MovableProxy(Movable movable) {
            this.movable = movable;
        }
    
        public void before() {
            System.out.println("before , do sth");
        }
    
        public void after() {
            System.out.println("after , do sth");
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
            Object o = method.invoke(movable, args);
            after();
            return o;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    主方法调用的时候:

    public class Main {
        public static void main(String[] args) {
            Movable tank = new Tank();
    
            //reflection 通过二进制字节码分析类的属性和方法
    
            Movable m = (Movable) Proxy.newProxyInstance(Movable.class.getClassLoader(),
                    new Class[]{Movable.class},
                    new MovableProxy(tank)
            );
    
            m.move();
            m.go();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    UML图如下:

    image

    Cglib

    JDK 自带的方式实现动态代理需要被代理对象实现一个接口, Cglib 不需要,使用示例:

    需要引入 Cglib 依赖

            <dependency>
                <groupId>cglibgroupId>
                <artifactId>cglibartifactId>
                <version>3.3.0version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中被代理的 Tank 类无需实现接口

    public class Tank {
        public void move() {
            System.out.println("tank move");
        }
        public void go() {
            System.out.println("tank go");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    import net.sf.cglib.proxy.Enhancer;
    
    public class Main {
    
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            //设置目标类的字节码文件
            enhancer.setSuperclass(Tank.class);
            //设置回调函数
            enhancer.setCallback(new MyMethodInterceptor());
    
            //这里的creat方法就是正式创建代理类
            Tank m = (Tank) enhancer.create();
            m.move();
            m.go();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class MyMethodInterceptor implements MethodInterceptor {
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            before();
            Object o = proxy.invokeSuper(obj, args);
            after();
            return o;
        }
    
        public void before() {
            System.out.println("before , do sth");
        }
    
        public void after() {
            System.out.println("after , do sth");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    JDK 动态代理与 CGLIB 代理的区别
    类型必须要实现接口支持拦截 public 方法支持拦截 protected 方法拦截默认作用域方法
    JDK 动态代理
    CGLIB代理

    无论是 JDK 自带动态代理还是 Cglib 实现动态代理,底层都是基于ASM操作二进制码,基于Java Instrumentation机制。

    代理模式的实际应用场景如下

    场景一

    在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、幂等、日志、缓存等,我们将这些附加功能与业务功能解耦,放到代理类中统一处理。

    场景二

    RPC 框架可以看成一种代理模式。

    场景三

    Spring 中的 JdkDynamicAopProxyCglibAopProxy

    可以使用配置强制使用 Cglib 动态代理

    UML 和 代码

    UML 图

    代码

    更多

    设计模式学习专栏

    参考资料

  • 相关阅读:
    Java(五):Java 基础语法
    统计假设检验
    matlab相机标定求得相机内参
    C++14读写锁demo-读写操作都在子线程中
    Java基础面试题
    hdfs 副本放置策略及快照功能简介
    7月 显卡选购指南矿难矿卡环境下选购选卡 AMD 6700 与6750xt 等解决方案
    OpenAI Kubernetes 相关博文读后笔记
    Python爬虫之xpath语法及案例使用
    【JavaScript】深入浅出理解事件循环
  • 原文地址:https://blog.csdn.net/hotonyhui/article/details/127757942