《知识点扫盲 · 动态代理》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗

🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

写在前面的话

之前几篇文章介绍 Spring 和 SpringMVC 源码中,经常看到反射机制和动态代理的源码,本篇展开介绍一下。

推荐文章 - 程序猿入职必会:
《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》
《程序猿入职必会(2) · 搭建具备前端展示效果的 Vue》
《程序猿入职必会(3) · SpringBoot 各层功能完善 》
《程序猿入职必会(4) · Vue 完成 CURD 案例 》
《程序猿入职必会(5) · CURD 页面细节规范 》
《程序猿入职必会(6) · 返回结果统一封装》
《程序猿入职必会(7) · 前端请求工具封装》
《程序猿入职必会(8) · 整合 Knife4j 接口文档》
《程序猿入职必会(9) · 用代码生成器快速开发》
《程序猿入职必会(10) · 整合 Redis(基础篇)》
《程序猿入职必会(11) · 整合 Redis 实战运用》

推荐文章 - 学会 SpringMVC 系列
《学会 SpringMVC 系列 · 基础篇》
《学会 SpringMVC 系列 · 剖析篇(上)》
《学会 SpringMVC 系列 · 剖析入参处理》
《学会 SpringMVC 系列 · 剖析出参处理》
《学会 SpringMVC 系列 · 返回值处理器》
《学会 SpringMVC 系列 · 消息转换器 MessageConverters》
《学会 SpringMVC 系列 · 写入拦截器 ResponseBodyAdvice》
《学会 SpringMVC 系列 · 剖析初始化》
《学会 SpringMVC 系列 · 参数解析器 ArgumentResolvers》


动态代理

技术简介

动态代理在Java中有着广泛的应用,比如 Spring AOP、RPC 远程调用、Java 注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。

代理模式

当我们谈及 Java 的 动态代理 或者是 静态代理 ,我们都很容易谈及到设计模式中的代理模式

代理模式给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问,起到对代理对象已有功能的增强。

代理模式存在三个角色:

1、抽象主题角色(Subject)

抽象主题角色指的是 真实对象代理对象 的公共的法所抽象出来的一个接口或者是抽象类。

在 Java 编程成,我们就可以认为是一个 接口 或者是 抽象类

2、真实主题角色(RealSubject)

真实主题角色指的是被代理对象。

3、代理主题角色(ProxySubject)

代理主题角色指的是代理对象。


两种代理

JDK 动态代理

适用场景:JDK 动态代理仅适用于接口代理。如果你的 Bean 实现了一个或多个接口,Spring 会使用 JDK 动态代理。

工作原理:JDK 动态代理通过反射机制创建一个实现了指定接口的代理类,并在调用方法时将调用转发到目标对象。
CGLIB 代理

适用场景:CGLIB 代理适用于没有实现接口的类,或者当你明确指定使用 CGLIB 代理时。CGLIB 通过生成目标类的子类来实现代理。

工作原理:CGLIB 通过字节码生成技术在运行时创建一个目标类的子类,并重写其方法,以实现代理功能。


JDK 动态代理

这里也来一个简单示例:

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义一个接口
interface Hello {
    void sayHello(String name);
}

// 实现 InvocationHandler 接口
class HelloInvocationHandler implements InvocationHandler {
    private final Hello target;

    public HelloInvocationHandler(Hello target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = method.invoke(target, args); // 调用目标对象的方法
        System.out.println("After method: " + method.getName());
        return result;
    }
}

// 目标类实现 Hello 接口
class HelloImpl implements Hello {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello, " + name);
    }
}

// 使用动态代理
public class ProxyExample {
    public static void main(String[] args) {
        Hello hello = new HelloImpl();
        Hello proxyInstance = (Hello) Proxy.newProxyInstance(
                hello.getClass().getClassLoader(),
                new Class<?>[]{Hello.class},
                new HelloInvocationHandler(hello)
        );

        proxyInstance.sayHello("World");
    }
}

上面的示例可以看出,核心就是 Proxy.newProxyInstance 方法,下面介绍一下。

【Proxy.newProxyInstance】

Proxy.newProxyInstance 是 Java 提供的一个静态方法,用于创建动态代理对象。这个方法属于java.lang.reflect.Proxy类,允许你在运行时创建一个实现了一组给定接口的新代理实例。这个代理实例可以在调用方法时,将调用转发到一个指定的处理器,通常是实现了 InvocationHandler 接口的对象。

java 复制代码
//参数1:ClassLoader loader:用于加载代理类的类加载器。通常可以使用目标类的类加载器。
//参数2:Class<?>[] interfaces:一个接口数组,代理对象将实现这些接口。
//参数3:InvocationHandlerh:一个实现了 InvocationHandler 接口的对象,负责处理代理实例上调用的方法。
//返回一个代理对象,该对象实现了指定的接口。
public static Object newProxyInstance(ClassLoader loader, 
                                      Class<?>[] interfaces, 
                                      InvocationHandler h)

【与反射机制的关系】

Proxy.newProxyInstance 方法与 Java 的反射机制密切相关。以下是它们之间的关系:

1、动态方法调用:在 InvocationHandler 的 invoke 方法中,使用反射来调用目标对象的方法。具体来说,Method.invoke(Object obj, Object... args) 方法是反射的一部分,它允许在运行时调用对象的方法。

2、接口的动态实现:通过 Proxy.newProxyInstance 创建的代理对象在运行时动态实现了指定的接口,而不是在编译时确定。这种动态特性是反射机制的一个重要应用。

3、方法信息:在 invoke 方法中,可以通过 Method 对象获取方法的元数据(如方法名、参数类型等),这也是反射机制的一部分。


CGLIB 动态代理

参考示例代码,如下所示:

java 复制代码
public class UserService {
    public void addUser(String username) {
        System.out.println("User " + username + " added.");
    }
}

public class CglibProxy implements MethodInterceptor {

    public Object createProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method: " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 调用父类的方法
        System.out.println("After method: " + method.getName());
        return result;
    }
}


public class Client2 {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        UserService userServiceProxy = (UserService) cglibProxy.createProxy(UserService.class);
        userServiceProxy.addUser("John Doe");
    }
}

JDK17需要添加如下VM选项:

--add-opens java.base/java.lang=ALL-UNNAMED


SpringBoot 与动态代理

Spring Boot 默认情况下使用 JDK 动态代理,除非满足以下条件才会使用 CGLib 动态代理:

1、目标类不是接口:

JDK 动态代理只能代理接口,如果目标类不是接口,Spring Boot 会使用 CGLib 动态代理。

2、proxyTargetClass 属性设置为 true:

在 @EnableAspectJAutoProxy 注解中,可以设置 proxyTargetClass 属性为 true,强制使用 CGLib 动态代理。

为什么 Spring Boot 默认使用 JDK 动态代理?

性能: JDK 动态代理通常比 CGLib 动态代理性能更高,因为它不需要生成额外的字节码。

简洁: JDK 动态代理更简洁,因为它只需要代理接口,而 CGLib 动态代理需要代理类。

为什在某些情况下使用 CGLib 动态代理?

代理非接口类: JDK 动态代理无法代理非接口类,而 CGLib 动态代理可以。

需要更强大的功能: CGLib 动态代理提供了更强大的功能,例如:可以代理私有方法。

总结:

Spring Boot 默认使用 JDK 动态代理,因为它性能更高、更简洁。但在某些情况下,例如代理非接口类或需要更强大的功能时,Spring Boot 会使用 CGLib 动态代理。


总结陈词

💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

相关推荐
陈大爷(有低保)1 分钟前
UDP Socket聊天室(Java)
java·网络协议·udp
kinlon.liu15 分钟前
零信任安全架构--持续验证
java·安全·安全架构·mfa·持续验证
王哲晓36 分钟前
Linux通过yum安装Docker
java·linux·docker
java66666888840 分钟前
如何在Java中实现高效的对象映射:Dozer与MapStruct的比较与优化
java·开发语言
Violet永存41 分钟前
源码分析:LinkedList
java·开发语言
执键行天涯42 分钟前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
Adolf_19931 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
Jarlen1 小时前
将本地离线Jar包上传到Maven远程私库上,供项目编译使用
java·maven·jar
蓑 羽1 小时前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap