Spring5应用之Cglib动态代理

2023年 10月 6日 31.7k 0

作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏
当前专栏:Spring5应用专栏_Aomsir的博客

前言

在上一篇文章中,我讲述了Spring动态代理底层具体实现之一的JDK动态代理,并在结尾抛出了本篇文章将要讲述的内容,本篇文章我将会着手于Spring动态代理底层具体实现的另一门技术之Cglib

Cglib动态代理

在上一章关于JDK动态代理的讨论中,我们确实注意到了其一个明显的限制:原始类和代理类都必须实现同一个接口,代理类方法添加额外功能并调用原始类方法。这种方式就像房屋中介:他们为租客提供服务,而租客可能并不知道背后的真正房东。但这种模式确实存在局限性,特别是当我们的系统设计中没有采用接口,或者原始类并没有实现任何接口而是直接写具体方法实现时。

那么,面对这种情况,我们怎么办?我们如何实现动态代理而不受到"必须实现接口"这一限制的约束?为了解决这个问题,Cglib(Code Generation Library) 应运而生。Cglib为我们提供了一种不基于接口的代理实现方式,它可以直接代理类。相对于JDK的代理方式,Cglib是基于继承父类(原始类)的,它会动态生成一个子类来覆盖原始类的方法,从而实现代理功能。这样既可以保证二者方法一致,这样既可以保证在代理类中提供新的实现,同时又调用了原始方法

这种方法提供了更大的灵活性,特别是对于那些没有接口的类。在接下来的章节中,我们将深入探讨Cglib,了解它是如何提供这种强大功能的,以及它与JDK动态代理的区别和优缺点

开发步骤

使用Cglib创建动态代理的过程在逻辑上与JDK动态代理非常相似,但主要差异在于原始类不需要实现任何接口。以下是具体的步骤:

  • 原始类的设计:首先,我们要设计并定义一个原始类。特别要注意的是,这里的原始类无需实现任何接口。这为那些基于纯类设计而非接口设计的应用提供了方便。
  • 定义增强功能:就像在JDK动态代理中一样,您需要确定您希望添加到原始类中的额外或增强功能。
  • 生成动态代理对象:使用Cglib的Enhancer对象可以为上一步中定义的原始类生成动态代理对象。为了实现此过程,确保项目中包含了Cglib的必要依赖。幸运的是这些依赖已经包含在spring-context库中,从而简化了集成过程
    在这里插入图片描述
  • public class UserService {
    
        private static final Logger log = LoggerFactory.getLogger(UserService.class);
    
        public void login(String name, String password) {
            log.info("name = {}, password = {}", name, password);
        }
    
        public void register(User user) {
            log.info("{}", user);
        }
    }
    
    public class TestCglibProxy {
    
        private static final Logger log = LoggerFactory.getLogger(TestCglibProxy.class);
    
        @Test
        public void test1() {
            // 创建原始对象
            UserService userService = new UserService();
    
            // 创建Cglib提供的Enhancer对象,用于设置好创建代理对象
            Enhancer enhancer = new Enhancer();
    
            // 设置类加载器
            enhancer.setClassLoader(TestCglibProxy.class.getClassLoader());
    
            // 设置被代理类(原始父类)
            enhancer.setSuperclass(UserService.class);
    
            // 设置方法拦截器
            MethodInterceptor methodInterceptor = new MethodInterceptor() {
                // 等同于InvocationHandler中的invoke方法
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    log.debug("log before");
    
                    Object ret = method.invoke(userService, objects);
    
                    log.debug("log after");
                    return ret;
                }
            };
            enhancer.setCallback(methodInterceptor);
    
            // 创建代理对象
            UserService userServiceProxy = (UserService) enhancer.create();
            userServiceProxy.login("Aomsir", "123456");
        }
    }
    

    总结

    经过前两篇文章的深入探讨,我们对Spring动态代理的底层实现有了更为全面的了解。其中,JDK动态代理和Cglib动态代理是两种最为核心的技术手段。

    JDK动态代理以接口为核心,通过Proxy.newInstance()方法为我们提供了创建代理对象的能力,要求原始类必须实现某个接口。而Cglib动态代理则更为灵活,不受接口的限制。通过Enhancer.create(),它直接生成原始类的子类,并在其中嵌入额外功能,从而实现代理。当然我们也可以通过设置让Spring在创建的时候只使用Cglib而不使用JDK

    Spring AOP聪明地整合了这两种机制,为开发者提供了无缝的体验。它默认采用JDK动态代理,但当遇到没有实现接口的原始类时,会智能切换到Cglib动态代理。

    现在,再回过头来重新审视 BeanPostProcessor,我们可以发现更多之前可能忽略的细节和深层次的关联。正是这些细节和关联,使得Spring AOP的实现如此高效且灵活,满足了开发者的各种需求

    
    
    

    参考文献

    • 孙哥孙帅suns说Spring5~学不会Spring? 因为你没找对人
    • Spring官方文档

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论