23种设计模式之代理模式--动态代理

本文详细介绍了代理模式的概念及其两种形式——静态代理与动态代理,并通过示例代码深入解析了JDK动态代理与CGLIB动态代理的工作原理及应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式(Proxy)

代理模式就是为其他对象提供一种代理功能以控制对被代理对象的访问,其作用就是通过一个中间层达到间接访问目标对象的目的。

代理模式分为静态代理和动态代理

静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。

动态:在程序运行时运用反射机制动态创建而成。

2、动态代理

在上一节中的静态代理中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象。动态代理有两种方式jdk动态代理和cglib动态代理。

2.1 jdk动态代理

使用jdk的代理机制必须要求被代理类实现一个具体接口,这样在生成的代理类才能知道重写哪些方法。jdk动态代理会生成一个被代理类所实现的接口的方法,如果没有实现任何接口,jdk代理机制则无法进行代理,解决办法就是使用cglib的代理机制进行代理。

java动态代理类主要位于Java.Long.reflect包下,一般主要涉及以下两个类:

  • Proxy:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
  • InvocationHandler:是代理实例调用处理程序实现的借口,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke方法中。

具体代码

//坦克类实现一个Movable借口,并实现里面的move方法
public class Tank implements Movable {
    @Override
    public void move() {
        System.out.println("Tank moving clsclacla...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Tank tank = new Tank();
        //System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
        //生成$Proxy0.class
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        /**
         * 参数一:代理类的类加载器
         * 参数二:被代理接口类数组
         * 参数三:要实现的处理方法
         */
        Movable m = (Movable) Proxy.newProxyInstance(Tank.class.getClassLoader(),
                new Class[]{Movable.class},
                new LogHandler(tank));
        m.move();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P5i8eKbb-1610814979735)(C:\Users\18801\Desktop\image-20210116233546384.png)]

public interface Movable {
    void move();
}
//处理程序
public class LogHandler implements InvocationHandler {
    Movable move;

    public LogHandler(Movable m) {
        this.move = m;
    }

    public void before(){
        System.out.println("method start...");
    }
    public void after(){
        System.out.println("method end...");
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object obj = method.invoke(move, args);
        after();
        return obj;
    }
}

根据下图的反编译文件$Proxy.class和相关源码可知代理对象m是如何调用LogHandler中的invoke方法的
在这里插入图片描述

总结

优点:代理对象无需实现接口,免去了编写很多代理类的烦恼,同时接口增加方法也无需再维护目标对象和代理对象,只需在事件处理器中添加对方法的判断即可。

缺点:代理对象不需要实现接口,但是目标对象一定要实现接口,否则无法使用JDK动态代理。

2.2 cglib动态代理

具体代码

public class Tank{
    public void move() {
        System.out.println("Tank moving clsclacla...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Tank.class);
        enhancer.setCallback(new TimeMethodInterceptor());
        Tank tank = (Tank)enhancer.create();
        tank.move();
    }
}
/**
 * 动态没生成代理对象
 * 实现 MethodInterceptor方法代理接口,创建代理类
 * 当访问代理中的方法时,委派给MethodInteceptor中的处理程序(intercept方法)进行出来,
 * 在处理程序中添加了业务逻辑和回掉了被代理目标中的方法。
 */
public class TimeMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("before");
            Object res = null;
            res = methodProxy.invokeSuper(o,objects);
            System.out.println("after");
            return res;
        }
}

注意:使用cglib需要引入

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

上一篇:静态代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值