Loading

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

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

作者: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 代理的区别

类型 必须要实现接口 支持拦截 public 方法 支持拦截 protected 方法 拦截默认作用域方法
JDK 动态代理
CGLIB代理

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

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

场景一

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

场景二

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

场景三

Spring 中的 JdkDynamicAopProxyCglibAopProxy

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

UML 和 代码

UML 图

代码

更多

设计模式学习专栏

参考资料

posted @ 2022-11-08 20:17  Grey Zeng  阅读(99)  评论(0编辑  收藏  举报