云计算百科
云计算领域专业知识百科平台

代理模式万字图解:让你快速掌握jdk和CGLIB动态代理

GoF之代理模式

一、什么是代理模式,究竟有什么用?

一句话说明

代理模式 = “用一个替身对象帮本体完成一件事,调用者只跟替身打交道;替身可以在不改动本体的情况下,附加额外功能(权限、缓存、事务、日志…)。”

举一个例子

你(客户端)想找明星(真实对象)拍电影,实际上先跟经纪人谈档期、谈钱;经纪人可以“拦截”一些不合理请求(权限控制),也可以帮明星安排行程(额外功能),但拍戏还是明星本人去拍。

为什么要有代理模式,解决什么问题?

当我需要对目标对象业务进行扩展的时候,但是又不想改变目标对象的代码。这时候就可以借助代理类去帮我对目标对象进行扩展,符合OCP原则。

代理模式的核心价值是 “在不侵入目标对象的前提下,对其访问进行控制或功能增强”。借助动态代理或 AOP,可在运行时动态织入新行为,从而更符合开闭原则。

代理模式是怎样的?

代理模式中的角色:

  • 代理类
  • 目标类
  • 公共的接口:客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。

在这里插入图片描述

代理模式实现的方法

  • 静态代理
  • 动态代理
  • 二、静态代理

    上面说了,一个代理模式,需要三个重要的角色,代理类、目标类和公共接口

    现在我们定义这先定义这三个重要的角色:

  • OrderService(目标类和公共接口)
  • OrderProxyStatic(代理类)
  • OrderServiceImpl(目标类)
  • 2.1 OrderService

    package com.wangxin.proxy.service;

    /**
    * @program: Spring_Project
    * @description: 订单的功能接口
    * @author: WangXin
    * @create: 2025-08-12 11:37
    **/

    public interface OrderService {
    /**
    * 生成订单
    */

    void generate();

    /**
    * 查看订单详情
    */

    void detail();

    /**
    * 修改订单
    */

    void modify();
    }

    2.2 OrderProxyStatic

    package com.wangxin.proxy.stat;

    import com.wangxin.proxy.service.OrderService;

    /**
    * @program: Spring_Project
    * @description: 静态代理模式
    * @author: WangXin
    * @create: 2025-08-12 11:40
    **/

    public class OrderProxyStatic implements OrderService {

    private OrderService OderService;

    public OrderProxyStatic(OrderService oderService) {
    OderService = oderService;
    }

    @Override
    public void generate() {
    long start = System.currentTimeMillis();
    OderService.generate();
    long end = System.currentTimeMillis();
    System.out.println(end start);

    }

    @Override
    public void detail() {
    long start = System.currentTimeMillis();
    OderService.detail();
    long end = System.currentTimeMillis();
    System.out.println(end start);
    }

    @Override
    public void modify() {
    long start = System.currentTimeMillis();
    OderService.modify();
    long end = System.currentTimeMillis();
    System.out.println(end start);
    }
    }

    2.3 OrderServiceImpl

    package com.wangxin.proxy.service.impl;

    import com.wangxin.proxy.service.OrderService;

    /**
    * @program: Spring_Project
    * @description: 具体实现类
    * @author: WangXin
    * @create: 2025-08-12 11:42
    **/

    public class OrderServiceImpl implements OrderService {
    @Override
    public void generate() {
    try {
    Thread.sleep(1234);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("订单已生成");
    }

    @Override
    public void detail() {
    try {
    Thread.sleep(2541);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("订单信息如下:******");
    }

    @Override
    public void modify() {
    try {
    Thread.sleep(1010);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("订单已修改");
    }
    }

    2.4 主函数调用代理方法

    OrderService orderService = new OrderServiceImpl();
    OrderProxyStatic proxyStatic = new OrderProxyStatic(orderService);
    proxyStatic.detail();

    2.5 静态代理的缺陷

    完成上面静态代理的代码,我们会发现我们扩展的功能是一样的,但是每个方法都要去重复这个扩展功能的书写,这样如果有100个方法,岂不是要写100扩展功能?而且一个扩展方法就表示一个代理类,需要扩展100个不同的方法岂不是需要100代理。因此,为解决这个问题,我们就出现动态代理

    三、动态代理

    在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。
    在内存当中动态生成类的技术常见的包括:

    • JDK动态代理技术:只能代理接口。
    • CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
    • Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。

    动态代理的原理和静态代理原理是一样的,只是动态代理是在内存中动态生成代理类,从而减少代理类的数量

    3.1jdk动态代理

    也是需要三个角色,只是代理类在内存中动态生成,其他两个角色和上面静态代理一样

    多了一个方法功能增强的类。基于反射

    3.1.2 主函数

    //创建目标对象
    OrderService target = new OrderServiceImpl();

    //创建代理对象
    OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new TimerInvocationHandler(target));

    //调用目标对象的增强后的方法
    orderServiceProxy.detail();
    orderServiceProxy.modify();
    orderServiceProxy.generate();

    3.1.3 new TimerInvocationHandler(target) 增强方法

    package com.wangxin.proxy.dynamiic;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;

    /**
    * @program: Spring_Project
    * @description: 计算运行时间的增强方法
    * @author: WangXin
    * @create: 2025-08-12 12:09
    **/

    public class TimerInvocationHandler implements InvocationHandler {

    private Object target;

    public TimerInvocationHandler(Object target) {
    this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    long start = System.currentTimeMillis();
    Object result = method.invoke(target, args);
    long end = System.currentTimeMillis();
    System.out.println("耗时"+(end start)+"毫秒");
    //表明如果有返回值,需要返回
    return result;
    }
    }

    InvocationHandler接口中有一个方法invoke,这个invoke方法上有三个参数:

    • 第一个参数:Object proxy。代理对象。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。
    • 第二个参数:Method method。目标方法。
    • 第三个参数:Object[] args。目标方法调用时要传的参数。

    3.2 CGLIB动态代理

    CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。

    为什么所代理的目标类不能使用final修饰?

    因为底层是用继承的方式实现,final修饰的类不能被继承和重写

    使用CGLIB,需要引入它的依赖:

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

    同样和静态代理一样三个角色,多了一个方法功能增强的类。基于反射

    主函数

    //创建字节码增强器
    Enhancer enhancer = new Enhancer();

    //指定继承哪个类
    enhancer.setSuperclass(OrderServiceImpl.class);

    //设置回调接口
    enhancer.setCallback(new TimerMethodInterceptor());

    //生成源码,并编译成class,加载到JVM中,最后生成代理类
    OrderService orderServiceProxy = (OrderService) enhancer.create();

    //调用目标对象的增强后的方法
    orderServiceProxy.detail();
    orderServiceProxy.modify();
    orderServiceProxy.generate();

    TimerMethodInterceptor 增强方法

    和JDK动态代理原理差不多,在CGLIB中需要提供的不是InvocationHandler,而是:net.sf.cglib.proxy.MethodInterceptor

    package com.wangxin.proxy.dynamiic;

    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;

    import java.lang.reflect.Method;

    /**
    * @program: Spring_Project
    * @description: CGLIB动态代理的实现
    * @author: WangXin
    * @create: 2025-08-12 12:20
    **/

    public class TimerMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    long start = System.currentTimeMillis();
    Object result = methodProxy.invokeSuper(o, objects);
    long end = System.currentTimeMillis();
    System.out.println("耗时"+(end start)+"毫秒");

    return result;
    }
    }

    MethodInterceptor接口中有一个方法intercept(),该方法有4个参数:
    第一个参数:目标对象
    第二个参数:目标方法
    第三个参数:目标方法调用时的实参
    第四个参数:代理方法

    3.3 JDK和CGLIB动态代理之间的区别

    维度JDK 动态代理CGLIB 动态代理
    技术基础 Java 反射 (java.lang.reflect.Proxy) 字节码框架 ASM
    代理目标 接口(必须) 类(也可以接口)
    实现方式 运行时生成一个 实现接口 的新类 运行时生成一个 继承目标类 的子类
    对 final 的要求 目标类/方法 不能是 final
    构造限制 必须提供接口 类必须有 无参构造器(或可用 Objenesis)
    性能 首次生成快、调用稍慢(反射) 首次生成慢、调用更快(方法索引)
    依赖 JDK 自带 需额外依赖 cglib(Spring 已内置)
    Spring AOP 规则 有接口 → 默认 JDK 无接口或强制 proxyTargetClass=true → CGLIB
    代理示例 Foo proxy = (Foo) Proxy.newProxyInstance(…) Enhancer.create(UserService.class, callback)
    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 代理模式万字图解:让你快速掌握jdk和CGLIB动态代理
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!