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


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

    作者:Grey

    原文地址:

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

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

    代理模式

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

    静态代理

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

    public class Main {
        public static void main(String[] args) {
            new Tank().move();
        }
    }
    

    假设需要在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");
        }
    }
    

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

    public class Main {
        public static void main(String[] args) {
            new TankLogProxy(new Tank()).move();
        }
    }
    

    即可实现在 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;
        }
    }
    

    主方法调用的时候:

    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();
        }
    }
    
    

    UML图如下:

    image

    Cglib

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

    需要引入 Cglib 依赖

            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.3.0</version>
            </dependency>
    

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

    public class Tank {
        public void move() {
            System.out.println("tank move");
        }
        public void go() {
            System.out.println("tank go");
        }
    }
    
    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();
        }
    }
    
    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");
        }
    }
    

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

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

    场景一

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

    场景二

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

    场景三

    Spring 中的 JdkDynamicAopProxyCglibAopProxy

    可以使用<aop:aspectj-autoproxy proxy-target-class="true">配置强制使用 Cglib 动态代理

    UML 和 代码

    UML 图

    代码

    更多

    设计模式学习专栏

    参考资料

  • 相关阅读:
    使用bootstrap中的toolTip插件时 最上方提示会被遮挡问题
    Vue中splice的使用
    由于eslint语法检查工具限制很严格,导致启动项目是报错的解决办吧(使用cmder工具时发生的错误)
    windows命令行工具—Cmder配置
    mui触发自定义事件(子页面返回传递给父级页面值)
    计算最长英语单词链
    周总结14
    找水王
    用户体验评价
    周总结13
  • 原文地址:https://www.cnblogs.com/greyzeng/p/16871054.html
Copyright © 2020-2023  润新知