深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

码农世界 2024-05-29 后端 87 次浏览 0个评论

深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

文章目录

    • 一、CGLIB 代理简介
      • 1.1 CGLIB 代理的基本原理和特点
      • 1.2 分析 CGLIB 如何通过字节码技术创建代理类
      • 二、深入分析 CglibAopProxy 类的结构
        • 2.1 CglibAopProxy 类结构
        • 2.2 CglibAopProxy 类源码
        • 三、CGLIB 代理对象的创建过程
          • 3.1 配置 Enhancer 生成代理对象
          • 3.2 探讨如何通过字节码生成技术嵌入拦截器逻辑到代理类中
          • 四、CGLIB 代理链的处理
            • 4.1 拦截器的调用顺序
            • 4.2 实现拦截器具体逻辑
            • 五、实践与应用
              • 5.1 编写自定义的 CGLIB 拦截器
              • 5.2 实现对非接口类的代理和增强功能

                一、CGLIB 代理简介

                1.1 CGLIB 代理的基本原理和特点

                CGLIB是一个强大的、高性能的代码生成库。它被广泛应用于AOP(面向切面编程)、ORM(对象关系映射)和其他一些框架中。

                CGLIB代理的基本原理:

                1. 创建代理类:CGLIB通过ASM字节码操作框架,在运行时动态生成目标类的子类。这个子类会继承自目标类。
                2. 方法拦截:在生成的子类中,会覆盖所有非final的方法。覆盖的方法会委托给一个用户定义的拦截器(MethodInterceptor),拦截器中包含了增强的代码。
                3. 调用流程:当调用代理类的方法时,实际上是在调用被覆盖的方法。这些方法内部会调用拦截器,拦截器再去调用原始类的相应方法。

                CGLIB代理的特点:

                1. 无需接口:CGLIB代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。
                2. 性能:CGLIB生成的代理类是目标类的子类,相比于JDK动态代理(接口代理),CGLIB代理通常有更好的性能,因为它直接调用父类的方法,减少了反射调用的开销。
                3. 灵活性:由于CGLIB代理是通过继承实现的,它无法代理final类和方法。但是,它提供了比JDK代理更高的灵活性,因为它可以代理任何类,而不受接口限制。
                4. 复杂性:CGLIB代理的实现比JDK动态代理复杂,因为它涉及到字节码生成和类加载机制。
                5. 兼容性:CGLIB代理通常与Spring框架结合使用,Spring AOP默认使用JDK动态代理,但如果目标对象没有实现接口,Spring AOP会自动切换到CGLIB代理。

                1.2 分析 CGLIB 如何通过字节码技术创建代理类

                CGLIB通过操纵字节码,创建出目标类的子类,并在子类中覆盖非final的方法,从而实现方法拦截和增强。

                CGLIB创建代理类的基本步骤:

                1. 确定目标类:首先要确定需要被代理的目标类。CGLIB代理不需要目标类实现任何接口,因为它是通过继承的方式来实现代理的。
                2. 创建Enhancer对象:Enhancer是CGLIB中的一个核心类,用于创建代理类。首先创建一个Enhancer实例,并设置其父类(即目标类)。
                3. 设置Callback:Callback是一个接口,用于定义代理类中覆盖方法的逻辑。通常使用MethodInterceptor接口,它允许我们在调用原始方法之前和之后插入自定义代码。将实现的Callback对象设置给Enhancer。
                4. 创建代理类:调用Enhancer的create()方法,CGLIB会使用ASM字节码操作框架来动态生成一个继承自目标类的子类。这个子类会覆盖所有非final的方法,并将调用委托给Callback对象。
                5. 使用代理类:create()方法返回的是一个代理类的实例,这个实例可以被当作目标类的实例来使用。当调用代理类的方法时,实际上会调用MethodInterceptor中的intercept()方法。
                6. 方法调用流程:在intercept()方法中,可以调用Method对象的invoke()方法来执行原始方法。这样,我们就可以在原始方法执行前后插入自定义的逻辑,实现方法的拦截和增强。

                二、深入分析 CglibAopProxy 类的结构

                2.1 CglibAopProxy 类结构

                • 成员变量:
                  • AdvisedSupport advised:存储了AOP配置信息的数据结构,如目标对象、切面等。
                  • Callback callback:CGLIB 回调对象,负责实现代理逻辑。
                  • 构造方法:
                    • CglibAopProxy(AdvisedSupport config):构造方法接收一个 AdvisedSupport 参数,用于设置AOP配置信息。
                    • 核心方法:
                      • getProxy(ClassLoader classLoader):生成代理对象的核心方法,接收一个 ClassLoader 参数用于加载代理类。
                      • createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks):使用 CGLIB 的 Enhancer 创建代理类,并返回代理对象的实例。
                      • proxy(ClassLoader classLoader, Callback[] callbacks):创建代理类并生成代理对象的实现逻辑, 使用 Enhancer 创建代理类,并指定 Callback 对象,完成代理类的生成和实例化。
                      • createEnhancer():创建 Enhancer 对象,用于生成代理类, Enhancer 是 CGLIB 中负责生成代理类的核心类。

                        2.2 CglibAopProxy 类源码

                        仅展示部分源码,其它源码会在下方解决其它问题时会出现,没有出现的,读者感兴趣可以自行去解读源码。

                        深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

                        三、CGLIB 代理对象的创建过程

                        代理对象的创建过程: 检查是否可以使用缓存的代理对象 -> 准备 CGLIB Enhancer -> 配置 Enhancer -> 设置回调处理器(Callback) -> 生成代理类字节码 -> 创建代理对象实例 -> 将代理对象缓存起来

                        3.1 配置 Enhancer 生成代理对象

                        Enhancer 对象通过调用 create() 方法来生成代理对象。

                        深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

                        createHelper() 方法用来实际创建代理对象。

                        深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

                        AbstractClassGenerator 对象通过调用 create() 方法,根据给定的键值(key)创建对象实例。

                        深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

                        3.2 探讨如何通过字节码生成技术嵌入拦截器逻辑到代理类中

                        createProxyClassAndInstance 负责创建代理类的实例,使用 CGLIB 技术创建代理对象,并将指定的拦截器回调方法应用于代理对象上。

                        深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

                        四、CGLIB 代理链的处理

                        通过ReflectiveMethodInvocation 类了解到在 Spring 框架中如何构建和执行代理链,以及拦截器如何在拦截器链中协作,以实现对目标方法的拦截和处理。

                        4.1 拦截器的调用顺序

                        在拦截器链中如何依次执行拦截器,并通过判断和调用不同的拦截器或目标方法来实现拦截和处理逻辑。

                        深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

                        invokeJoinpoint()用于执行目标方法,如果拦截器链中已经没有下一个拦截器了,或者拦截器中的某个拦截器选择不继续执行拦截器链,那么就会调用这个方法来执行目标方法。

                        深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

                        4.2 实现拦截器具体逻辑

                        定义了方法拦截器的标准,任何实现该接口的类都可以作为 Spring AOP 中的拦截器,用于在目标方法执行前后添加额外的逻辑。

                        深度解析 Spring 源码:探秘 CGLIB 代理的奥秘

                        五、实践与应用

                        5.1 编写自定义的 CGLIB 拦截器

                        假设有一个简单的服务类 UserService,其中包含一些方法,希望能够在调用这些方法之前和之后记录日志。使用CGLIB来实现一个拦截器,记录方法调用的开始和结束时间。

                        1. 服务类 UserService,模拟创建和更新用户信息。
                        public class UserService {
                            public void createUser(String username) {
                                System.out.println("Creating user: " + username);
                                // 模拟创建用户的逻辑
                            }
                            public void updateUser(String username) {
                                System.out.println("Updating user: " + username);
                                // 模拟更新用户的逻辑
                            }
                        }
                        
                        1. 自定义的CGLIB拦截器,用于记录方法调用的开始和结束时间。
                        /**
                         * 创建 LoggingInterceptor 类,实现 MethodInterceptor 接口
                         */
                        public class LoggingInterceptor implements MethodInterceptor {
                            
                            /**
                             * 参数:obj 是被代理的对象实例
                             *      method 是被调用的方法对象
                             *      args 是方法的参数数组
                             *      proxy 是用于调用父类(被代理类)方法的代理对象
                             */
                            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                                // 获取方法调用开始时的时间戳
                                long startTime = System.currentTimeMillis();
                                System.out.println("Method " + method.getName() + " start at: " + startTime);
                                // 调用被代理类的原始方法,而不是代理对象的方法,以避免循环调用
                                Object result = proxy.invokeSuper(obj, args);
                                // 获取方法调用结束时的时间戳
                                long endTime = System.currentTimeMillis();
                                System.out.println("Method " + method.getName() + " end at: " + endTime);
                                // 方法执行所花费的时间
                                System.out.println("Method " + method.getName() + " execution time: " + (endTime - startTime) + " milliseconds");
                                // 调用原始方法后的返回值
                                return result;
                            }
                            public static void main(String[] args) {
                                UserService userService = new UserService();
                                // 使用CGLIB的 Enhancer 类创建了 UserService 类的代理对象,并将拦截器设置为回调方法
                                Enhancer enhancer = new Enhancer();
                                // 设置了要代理的目标类是 UserService
                                enhancer.setSuperclass(UserService.class);
                                // 指定了在方法调用时应该执行的拦截逻辑
                                enhancer.setCallback(new LoggingInterceptor());
                                // 创建代理对象,将会在方法调用时执行我们定义的拦截逻辑
                                UserService userServiceProxy = (UserService) enhancer.create();
                                // 调用代理对象的 createUser 和 updateUser 方法来触发拦截器的拦截逻辑
                                userServiceProxy.createUser("John Doe");
                                userServiceProxy.updateUser("Jane Smith");
                            }
                        }
                        //输出结果:
                        Method createUser start at: 1621802728000
                        Creating user: John Doe
                        Method createUser end at: 1621802728000
                        Method createUser execution time: 0 milliseconds
                        Method updateUser start at: 1621802728000
                        Updating user: Jane Smith
                        Method updateUser end at: 1621802728000
                        Method updateUser execution time: 0 milliseconds
                        

                        5.2 实现对非接口类的代理和增强功能

                        实现对非接口类的代理和增强功能通常使用 Spring AOP来实现,提供了一种便捷的方式来在方法执行前、执行后、方法抛出异常时等时机插入特定逻辑,而无需修改原始类的代码。

                        假设有一个订单管理系统,其中包含一个 OrderService 类,该类负责处理订单相关的业务逻辑,比如创建订单、更新订单状态等。希望在处理订单相关业务时,记录日志并统计方法执行时间。

                        1. 切面类 OrderAspect。
                        @Aspect
                        @Component
                        public class OrderAspect {
                            /**
                             * 切面方法,用于实现切面的逻辑 -> 表示正在执行目标方法之前
                             * 接受一个 JoinPoint 参数,连接点 -> 被增强的目标方法
                             */
                            @Before("execution(* com.example.service.OrderService.*(..))")
                            public void logBefore(JoinPoint joinPoint) {
                                System.out.println("Before executing method: " + joinPoint.getSignature());
                            }
                             /**
                             * 切面方法,用于实现切面的逻辑 -> 表示目标方法执行完成后
                             * 接受一个 JoinPoint 参数,连接点 -> 被增强的目标方法
                             */
                            @After("execution(* com.example.service.OrderService.*(..))")
                            public void logAfter(JoinPoint joinPoint) {
                                System.out.println("After executing method: " + joinPoint.getSignature());
                            }
                        }
                        
                        1. 配置类中启用 Spring AOP 功能。
                        /**
                         * 标识这个类是一个配置类 -> 告诉 Spring 容器如何配置应用程序上下文
                         * 启用了 AspectJ 自动代理 -> 告诉 Spring 在运行时生成 AOP 代理以支持 @AspectJ 切面
                         * 指示 Spring 在包 com.example 及其子包中扫描组件 -> 自动发现并注册带有 @Component、@Service、@Repository 和 @Controller 注解的 bean
                         */
                        @Configuration
                        @EnableAspectJAutoProxy
                        @ComponentScan(basePackages = "com.example")
                        public class AppConfig {
                            
                        }
                        
                        1. OrderService 类。
                        /**
                         * 调用 OrderService 类的 createOrder() 或 updateOrderStatus() 方法时,OrderAspect 切面中定义的增强逻辑会在方法执行前后生效,从而实现了对非接口类的代理和增强功能
                         */
                        @Service
                        public class OrderService {
                            public void createOrder() {
                                // 模拟创建订单的业务逻辑
                                System.out.println("Creating order...");
                            }
                            public void updateOrderStatus() {
                                // 模拟更新订单状态的业务逻辑
                                System.out.println("Updating order status...");
                            }
                        }
                        // 输出结果:
                        Before executing method: public void com.example.service.OrderService.createOrder()
                        Creating order...
                        After executing method: public void com.example.service.OrderService.createOrder()
                        Before executing method: public void com.example.service.OrderService.updateOrderStatus()
                        Updating order status...
                        After executing method: public void com.example.service.OrderService.updateOrderStatus()
                        

                        对乐于苦斗的人来说,苦斗不是憾事,而是乐事

转载请注明来自码农世界,本文标题:《深度解析 Spring 源码:探秘 CGLIB 代理的奥秘》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,87人围观)参与讨论

还没有评论,来说两句吧...

Top