JDK 动态代理

jdk动态代理和cglib动态代理对比 JDK 动态代理:只代理接口(interface),无法代理类。它使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现动态代理。 CGLIB 动态代理:可以代理类和接口。它通过生成目标类的子类来创建代理对象,因此可以代理没有实现接口的类。 阅读详情

一、序言

今天和大家聊聊 JDK 动态代理。

二、什么是 JDK 动态代理

在 Java 中,动态代理是一种强大的机制,允许在运行时创建代理类和对象,以拦截并处理对目标对象的方法调用。这种机制为实现各种横切关注点(cross-cutting concerns)提供了灵活且强大的手段,如日志记录、性能监控、事务管理等。
在 JDK 中,动态代理的实现依赖于 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler接口。通过 Proxy 类的 newProxyInstance 方法,我们可以动态地创建代理对象,并为其指定一个 InvocationHandler 对象,用于处理代理对象上的方法调用。
动态代理的核心在于 InvocationHandler 接口,它定义了一个方法 invoke,用于处理代理对象上的方法调用。通过实现 InvocationHandler 接口,我们可以在代理对象的方法调用前后执行额外的逻辑,如日志记录、权限检查等。这种方式使得我们能够将横切关注点与业务逻辑分离,提高了代码的可维护性和可扩展性。
总的来说,JDK 动态代理为 Java 开发者提供了一种优雅且灵活的方式来实现 AOP 和其他横切关注点,使得代码更加模块化、可重用和易于理解。

三、JDK 动态代理实践

现假设一个业务场景:我们需要在 UserService 中方法记录一些自定义的日志信息。

  1. 需要代理的类

    // 接口
    public interface UserService {
        User getUser(int userId);
    }
    
    // 实现类
    public class UserServiceImpl implements UserService {
    
        @Override
        public User getUser(int userId) {
            // 具体业务逻辑...
        }
    }
    
  2. 使用 JDK 代理完成对上述类中方法的日志信息代理

    public class LogDynamicProxy implements InvocationHandler {
    
        // 需要代理的目标对象
        private Object target;
    
        // 构造器
        public LogDynamicProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 定义代理之后的结果变量
            Object result;
    
            // 执行被代理对象原始的方法
            result = method.invoke(target, args);
    
            // 记录日志(自行定义记录日志的业务逻辑)
            System.out.println("记录日志...");
    
            // 返回代理之后的结果
            return result;
        }
    }
    
  3. 使用代理对象

    public void test() {
        // 实例化 UserService
        UserService userService = new UserServiceImpl();
    
        // 获取 InvocationHandler
        InvocationHandler handler = new LogDynamicProxy(userService);
    
        // 获取 UserService 的代理对象
        Object o = Proxy.newProxyInstance(
            userService.getClass().getClassLoader(),
            userService.getClass().getInterfaces(),
            handler
        );
    
        // 将 UserService 的代理对象转换成 UserService
        UserService proxy = (UserService) o;
    
        // 执行 UserService 代理之后的方法
        User user = proxy.getUser(1);
    }
    

通过 JDK 动态代理,我们发现:记录日志的操作不需要写在 UserService 的实现类中(记录日志的代码和业务逻辑的代码实现了解耦)。

四、JDK 动态代理工作流程

未命名文件.png
由上面的示例我们可以分析出,JDK 代理的工作流程为:

  1. 需要被代理的对象将需要代理的操作(例如:记录日志)委托给 InvocationHandler
  2. Proxy 类根据需要被代理的对象和委托的 InvocationHandler 生成具体的代理实例
  3. 最后,执行代理实例中的代理方法

五、JDK 动态代理源码分析

5.1 InvocationHandler 接口

public interface InvocationHandler {

    // proxy:代理对象本身,可以使用它来调用被代理对象的方法
    // method:被代理的方法对象,你可以通过它获取方法的名称、参数等信息
    // args:方法调用时传入的参数数组
    // throws Throwable:这个方法声明了可能抛出 Throwable 类型的异常,表示在方法调用过程中可能会出现的异常
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;
}

上述代码是 InvocationHandler 接口的源码,其内容只有一个 invoke() 方法。

5.2 Proxy#newProxyInstance()

// 这是一个静态方法,用于创建动态代理对象。它接收三个参数
// loader:一个 ClassLoader 对象,用于加载代理类
// interfaces:一个 Class 对象数组,表示被代理对象实现的接口
// h:一个 InvocationHandler 对象,用于处理代理对象的方法调用
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
throws IllegalArgumentException
{
    // 检查传入的 InvocationHandler 对象是否为 null
    // 如果是,则抛出 NullPointerException 异常
    Objects.requireNonNull(h);

    // 复制传入的接口数组,以防止对原始数组进行修改
    final Class<?>[] intfs = interfaces.clone();
    
    // 获取当前的安全管理器
    final SecurityManager sm = System.getSecurityManager();
    
    // 如果存在安全管理器,则检查对代理类的访问权限
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    // 获取代理类。这个方法会根据传入的类加载器和接口数组来查找或生成代理类
    Class<?> cl = getProxyClass0(loader, intfs);

    try {
        // 如果存在安全管理器,则检查对代理类的创建权限
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        // 通过反射获取代理类的构造函数
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        
        // 如果代理类的构造函数不是公共的,则通过特权操作设置其可访问性
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }

        // 调用构造函数创建代理对象
        // 传入 InvocationHandler 对象作为参数
        // 返回新创建的代理对象
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

以上是 Proxy#newProxyInstance()方法的源码。我们可以分析出其核心流程为:

在这里插入图片描述

  1. 环境检查:在创建代理对象之前,会进行一系列的环境检查,包括权限检查等。
  2. 生成代理类:通过 getProxyClass0() 方法生成代理类。该方法根据传入的类加载器和接口数组,查找或者生成代理类。
  3. 生成代理对象:根据生成的代理类来创建代理对象。代理对象实现了指定的接口,并将方法调用委托给指定的 InvocationHandler 处理器。

六、FAQ

6.1 JDK 动态代理为什么必须实现接口

public final class $Proxy extends Proxy implements UserService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final User getUser(int var1) throws  {
        try {
            return (User)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.foxconn.service.UserService").getMethod("getUser", Integer.TYPE);
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

上面的代码是 JDK 动态代理生成的代码。可以发现生成的代理类默认继承了 Proxy 类。 在 Java 中,一个类只能继承自一个父类,但是可以实现多个接口。这就意味着如果要创建一个动态代理类,只能通过接口去实现而不能使用类去继承。

opencv+nvcodec实现视频硬解码 最近遇到一个新项目,AI推理在上,为了方便和节省成本的考虑决定研究下模块。根据官网的说法显卡具有独立的和模块,所以理论上和是独立互不干涉的。以前的项目都只是把显卡当成推理工具,没有将它的功能利用起来,本身也是一种浪费。事实真的有这么丝滑吗?看到这里,如果你真的觉得是白叫的那就真的是了,事实究竟如何,请听我娓娓道来。NVCODEC全称是 NVIDIA VIDEO CODEC,是NVIDIA为绝大多数显卡配置的硬件编解码单元。不一定是所有显卡都有,一般来讲越新的显卡硬件编解码就越强,支持的格式也就越新。 阅读详情

相关推荐

kali破解wifi之airmon-ng 工具利用

......

qq_42378173的博客 1万+

Java——JDK动态代理

动态代理是指代理类对象在程序运行时由JVM根据反射机制动态生成的。动态代理不需要定义代理类的,java源文件。动态代理其实就是jdk运行期间,动态创建class字节码并加载到JVM。动态代理的实现方式常用的有两种:使用JDK代理,与通过CGLlB动态代理。

m0_73671382的博客 7677

2026.5.3:Ubuntu26.04下uv所需的环境变量pyproject.toml

本文档提供了Python开发环境的配置指南。主要包括: 设置环境变量(HF_ENDPOINT、UV_PYTHON_INSTALL_MIRROR等) 创建pyproject.toml配置文件,定义项目元数据(名称、版本等) 指定依赖项(torch系列库)及其安装源(PyTorch官方CUDA 12.8镜像) 配置Python版本要求(>=3.13,<3.14) 设置平台限制(Linux/Windows) 该配置适用于需要PyTorch CUDA 12.8支持的项目开发环境搭建。

拥抱具身智能,昇腾Ascend C 586

JDK动态代理详解

可能很多小伙伴首次接触动态代理这个名词的时候,或者是在面试过程中被问到动态代理的时候,不能很好的描述出来,动态代理到底是个什么高大上的技术。不方,其实动态代理的使用非常广泛,例如我们平常使用的Spring中的注解,其依赖于AOP,而AOP的底层实现便是动态代理,看到这里,是不是更有兴趣去了解动态代理了呢?动态代理:可以分解为“动态”+“代理”。所以,静态代理存在一定的局限性,不能很好的满足需求的千变万化,动态代理的出现,就是为了解决这些局限性。

StoNENee的博客 4729

【002】C++ 语言核心:关键字深度解析与分类指南

auto:自动类型推导bool:布尔类型break:跳出循环或switch语句case:switch语句分支char:字符类型class:定义类const:常量continue:结束当前循环,开始下一次循环default:switch语句默认分支delete:删除对象do:do-while循环double:双精度浮点数类型else:if语句的否定分支enum:枚举类型explicit:显式构造函数export:导出符号extern:声明外部变量或函数。

Lion_Long的博客 1248

一个中科大差生的8年程序员工作总结

今年终于从大菊花厂离职了,离职前收入大概60w不到吧,在某乎属于比较差的,今天终于有空写一下自己的职场故事,也算是给自己近8年的程序员工作做个总结复盘。近8年有些事情做对了,也有更多事情做错了,在这里记录一下,希望能够给后人一些帮助吧,也欢迎私信交流。文笔不好,见谅,有些细节记不清了,如果有出入,就当是我编的这个故事吧。 PS:有几个问题先在这里解释一下,评论就不一一回复了 1、关于差生,我本人在科大时确实成绩偏下,差生主要讲这一点,没其他意思。 2、因为买房是我人生中的大事,我认为需要记录和总结一下

陈小房的自我修炼 5万+

Java-JDK动态代理

介绍:我们需要掌握的程度动态代理(理解) 基于反射机制掌握的程度:1.什么是动态代理?2.动态代理能够做什么?后面我们在用Spirng和Mybatis的时候,要理解怎么使用的.代理,在我们日常生活之中就有体现,代购,中介,换ip,商家等等.比如有一家美国的大学,可以对全世界招生.留学中介(代理 )留学中介(代理):帮助这家美国的学校招生,中介是学校的代理中介是代替学校完成招生功能代理特点中介和代理他们要做的事情是一致的:招生中介是学校代理,学校是目标。

zhxingqi的博客 6358

YOLOv11涨点改进| TGRS 2026 顶刊| 注意力改进篇| 引入MSEA多尺度边缘感知注意力,助力红外小目标检测、遥感目标检测、工业缺陷检测、图像去雨雾任务高效涨点

🔥本文提出了一种基于MSEA多尺度边缘感知注意力的YOLOv11改进模型,通过增强网络对边缘、纹理和细粒度信息的感知能力,显著提升复杂背景下的目标检测性能。MSEA模块采用多尺度空洞卷积提取上下文特征,并通过边缘监督和注意力融合机制强化目标边界信息。实验表明该方法在小目标、边界模糊目标和复杂纹理目标检测任务中表现优异,尤其在遥感、工业缺陷和红外检测等场景具有显著优势。文章详细介绍了MSEA的结构原理、核心代码实现、模块配置方法以及三种YOLOv11改进方案.

Ai缝合怪、助力小伙伴高效发论文 179

Spring高手之路17——动态代理的艺术与实践

本文深入分析了JDK和CGLIB两种动态代理技术在Spring框架中的应用。讨论了动态代理的基础概念,通过实例展示了如何实现和应用这两种方法,并比较了它们的性能差异及适用场景。进一步,探讨了在动态代理中实现熔断限流和日志监控的策略,以及如何利用动态代理优化Spring应用的设计和功能。

卓越无关环境,保持空杯心态——靡不有初,鲜克有终 1万+

Windows 下 Codex 安装全记录:从零到一的保姆级图文教程

本文主要介绍了Windows 下 Codex 安装全记录:从零到一的保姆级图文教程,希望能对学习使用 Codex 的同学们有所帮助。 文章目录 1. 背景 2. 实战教程 2.1 方案一 2.2 方案二 3. 报错解决方案

weixin_43178406的博客 14万+

【ARM Trace32(劳特巴赫) 高级篇 20 -- SNOOPer 使用介绍】

在 Lauterbach TRACE32 调试系统中,Snooper是一种特殊的监视工具,它能够在不中断处理器执行的情况下监视和记录特定内存地址的数据变化。这对于系统级调试、性能分析、数据流监视以及实时系统的问题追踪特别有用,尤其是在无法使用中断的情况下。当你设置了一个或多个Snooper时,TRACE32 硬件将不断地监视指定的内存地址,一旦发生了读写操作,就会将相关信息记录下来。这样你就可以看到某个地址的数据是什么时候以及如何被修改的,而不会影响目标系统的运行。

CodingCos的博客 2763

JDK动态代理

个人理解: 就是当你写了一个接口,里面有方法,然后写了实现类实现方法,完成方法逻辑,然后有一天,想对这个方法进行修改,但是不想改源码,所以有两种方式可以实现。第一种是静态代理,创建一个类继承实现类,然后对方法进行修改,这样太局限,因为只能针对特定的类增强方法,有100个实现类就要创建100个子类去实现。第二种方式是动态代理,可以动态地生成代理类,这是可以接受的。 这篇文章记录的JDK动态代理 2.创建代理类 需要实现InvocationHandler接口,重写invoke方法,这里可以对方法进行增强。 3

weixin_46666822的博客 1万+

【ROS2】RViz2加载URDF模型文件

1)运行RViz2rviz22)添加组件:RobotModel3)选择通过文件添加4)选择URDF文件,此时会报错,需要修改Fixed Frame为map即可

郭老二 1449

简单实现jdk的动态代理

简单实现jdk的动态代理 jdk动态代理的使用 package com.util; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { //产生代理对象 public static ...

小王同学的博客 2070

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

SpringAOP JDK动态代理

目前我本人正在学习SpringFramework的知识,这也是这个专栏的主题。我前面的几篇博文中,简单的认识了一下SpringFramework,记录了SpringFramework的环境搭建,记录了SpringIOC如何加载配置文件,记录了SpringIOC三种Bean对象的实例化方法。还记录了SpringIOC手动装配(注入)的方法之一:set方法注入。还有另外三个不太常用的SpringIOC手动注入的方法。其实目前大部分都使用的是自动注入的方法,手动注入使用的不多。就算是使用手动注入的方法,使用的主流

weixin_46281472的博客 3303

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

JDK动态代理模式详解

常用的代理模式有,jdk动态代理s和CGLIB动态代理模式,JDK动态代理是java.lang.reflect.*包提供的方式,它必须提供一个接口才能产生代理对象。 下面讲解jdk动态代理,如下图: 假设有这样一个场景,你的公司是一个软件公司,你是一位软件工程师,客户带着需求过来,显然不会先去直接找你,而是去找一个叫商务对象的人,此时商务就是代理你的人,同时也是与客户交谈的人。 ...

Dream_it_possible!的博客 1532

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

jdk 动态代理_JDK动态代理为什么不能代理类

关于JDK动态代理,想必小伙伴都知道,它只能代理接口!但是有的小伙伴可能会有疑问,为什么它只能代理接口,不能代理类呢?这里借助某位技术大佬的话--”源码面前,了无秘密“,下面咱们就一起看下JDK动态代理源码实现,最后再探讨下JDK动态代理为什么不能代理类,OK,话不多说,Let's go~ 本文分为两部分来分析JDK动态代理,首先看下JDK动态代理使用,然后分析动态代理源码实现及探讨下其为什么不能...

weixin_39870150的博客 946

jdk动态代理和cglb动态代理

动态代理和静态代理都是代理模式的实现方式,其主要区别在于代理类生成的时机和方式。静态代理是在编译时就确定了代理类的代码,在程序运行前就已经存在了代理类的class文件。代理类与委托类的关系在编译时就已经确定,因此被称为静态代理。在静态代理中,代理类需要实现与委托类相同的接口或者继承委托类的父类,以便能够对委托类进行代理操作。动态代理是在程序运行时生成代理类的代码,代理类通过反射机制动态生成。动态代理不需要像静态代理一样实现与委托类相同的接口或继承委托类的父类,它可以实现任意接口。

一个菜鸡的博客 1069

Jdk动态代理和Cglib动态代理的区别

最后我们总结一下JDK动态代理和Gglib动态代理的区别:1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

qq_48157004的博客 1224

JDK动态代理和CGLIB动态代理的区别

在 Spring AOP 中,和是两种核心的代理实现方式,它们的区别可以从多个维度进行对比。finalfinal:Spring 5.x 后 CGLIB 性能优化明显,差距缩小。final// 必须实现 UserService 接口System.out.println("[JDK代理]前置处理");System.out.println("[JDK代理]后置处理");});// 直接代理普通类System.out.println("[CGLIB代理]前置处理");// 调用父类方法。

m0_75236543的博客 1071

JDK动态代理和CGLib动态代理的区别

本文介绍Java中JDK动态代理和CGLib动态代理的区别。

IT利刃出鞘的博客 2526
上一篇: Seata 整合 Nacos 2.x
下一篇: 分布式锁(Redis)
知行小栈
博客等级 码龄9年 2506粉丝 153原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值