CGLIB 是 Spring 实现 AOP 的核心底层技术之一,它基于 ASM 字节码框架,在运行时生成目标类的子类来实现代理。相比于 JDK 动态代理(基于接口),CGLIB 可以代理普通的 Java 类,灵活性更高。

springboot 【spring-core-7.0.2.jar】已经将cglib,asm相关的代码作为其core代码的一部分

从Springboot 2.x以后,默认统一使用 CGLIB, 针对有接口的则切换到JdkDynamicAopProxy,原因如下:

  1. 适用性更强(无需强制实现接口): CGLIB 通过生成子类来实现代理,不需要你的业务类必须实现某个接口。这符合现代开发中更倾向于直接面向类编程的趋势,减少了开发约束。
  2. 性能更优: JDK 动态代理底层依赖 Java 反射(InvocationHandler),性能相对较低;而 CGLIB 底层使用 ASM 框架直接操作字节码,通过 FastClass 机制进行方法调用,跳过了反射的性能损耗,执行效率更高。
    在 Spring AOP 中,使用注解(如 @Transactional)来声明需求,Spring 底层则会选择使用 JDK 动态代理CGLIB 来实现这个需求

CGLIB底层实现原理

CGLIB 并不是 Spring 自己写的,而是一个强大的第三方字节码生成库(底层依赖 ASM 框架)。

  • 核心机制:继承。
    CGLIB 在运行时动态地生成一个被代理类的子类final 类除外,因为不能被继承)。
  • 拦截逻辑:
    这个生成的子类会**重写(Override)**父类中所有的非 final 方法。在重写的方法中,它会插入拦截器逻辑(MethodInterceptor)。
  • 执行流程:
    当你调用代理对象的方法时,实际上调用的是子类重写后的方法。子类在执行时,会先通知拦截器(执行切面逻辑,如日志、事务),然后再通过反射调用父类(即目标对象)的原始方法。

简单总结: CGLIB 就是通过“继承你的类,重写你的方法”来实现功能增强的

与JDK动态代理的区别

对比维度 JDK 动态代理 CGLIB 动态代理
实现机制 Java 反射 API (Proxy + InvocationHandler) 字节码操作(ASM 框架,生成子类)
对接口的依赖 必须实现接口 不需要实现接口(直接代理类)
性能 相对较低(依赖反射调用) 较高(通过 FastClass 调用,接近原生)
限制 只能代理接口中定义的方法 不能代理 final 类或 final 方法
Spring Boot 默认 非默认 默认

如何配置代理方式

  1. 通过配置文件设置
# 默认使用CGLIB,
# 如果设置为False,则切换为JAVA动态代理,但如果目标类没有实现接口,Spring 会自动降级回 CGLIB
spring:
  aop:
    proxy-target-class: true
  1. 通过注解设置 @EnableAspectJAutoProxy(proxyTargetClass = true)

CGLIB何时生成代理类

CGLIB 生成代理类是在 运行时(Runtime) 完成的,而不是在编译阶段,CGLIB 是一种运行时代码生成技术

当你的 Spring Boot 应用启动,或者程序执行到动态代理创建逻辑时(例如调用 Enhancer.create()),CGLIB 才会通过 ASM 框架在内存中动态生成一个新的字节码文件(即代理类),类名类似UserServiceImpl$$EnhancerByCGLIB$$e073c71b

CGLIB 的核心组件是 Enhancer。当你调用 enhancer.create() 时,底层发生了以下事情:

  1. 触发: 程序运行到创建代理对象的代码。
  2. 生成字节码: CGLIB 使用 ASM 库在 JVM 内存中,根据你的目标类(父类),动态编写一个新的类(子类)的字节码。
  3. 加载: 通过类加载器将这个新生成的字节码加载到 JVM 中。
  4. 实例化: 创建该代理类的实例并返回。

Springboot 代理的自动选择策略

在 Spring Boot(实际上是 Spring Framework 的 AOP 模块)中,这个自动选择策略(即:判断是用 JDK 动态代理还是 CGLIB)的源码核心位于 DefaultAopProxyFactory 类中

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {  
    public static final DefaultAopProxyFactory INSTANCE = new DefaultAopProxyFactory();  
    private static final long serialVersionUID = 7930414337282325166L;  
  
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  
        if (!config.isOptimize() && !config.isProxyTargetClass() && config.hasUserSuppliedInterfaces()) {  
            return new JdkDynamicAopProxy(config);  
        } else {  
            Class<?> targetClass = config.getTargetClass();  
            if (targetClass == null && config.getProxiedInterfaces().length == 0) {  
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");  
            } else {  
                return (AopProxy)(targetClass != null && !targetClass.isInterface() && !Proxy.isProxyClass(targetClass) && !ClassUtils.isLambdaClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));  
            }  
        }  
    }  
}

示例:

场景 目标类实现的接口 hasUserSuppliedProxyInterfaces
结果
Spring 的决策
场景 A 实现了 UserService (你的业务接口) true 默认使用 JDK 动态代理
场景 B 什么接口都没实现 false 使用 CGLIB 代理
场景 C 实现了
ApplicationContextAware
false 使用 CGLIB 代理 (因为这是 Spring 内部接口)
场景 D 实现了
SpringProxy (极少情况)
false 使用 CGLIB 代理 (因为这是 Spring 自己的标记)

调用链路

在 Spring Boot 启动过程中,这个逻辑是在 Bean 初始化的后置处理器中被触发的:

  1. 入口AbstractAutoProxyCreator.postProcessAfterInitialization()
  2. 包装判断wrapIfNecessary() 方法被调用。
  3. 创建代理:如果发现当前 Bean 需要被 AOP 增强,则调用 createProxy()
  4. 工厂决策ProxyFactory 会调用上述的 DefaultAopProxyFactory.createAopProxy() 来获取具体的代理实例。
    1. ProxyFactory 本身不直接实现创建逻辑,它继承自 ProxyCreatorSupport,而 ProxyCreatorSupport 持有一个非常关键的组件:AopProxyFactory
    2. 当你调用 proxyFactory.getProxy() 时,它内部会调用父类 ProxyCreatorSupportcreateAopProxy() 方法。
    3. ProxyCreatorSupport.createAopProxy(): 它负责从配置中获取 AopProxyFactory 并调用其 createAopProxy() 方法
    4. 默认情况下,ProxyCreatorSupport 中持有的 AopProxyFactory 实例就是 DefaultAopProxyFactory

何时使用Objenesis

在 Spring 框架中,objenesis 相关的包(主要位于 org.springframework.objenesis)提供了一个非常核心且底层的功能:绕过构造函数来实例化对象

关于选择 Objenesis 还是 反射(Reflection) 来实例化对象,主要发生在 AOP 代理创建和某些特殊 Bean 的实例化过程中。

Spring 的选择策略可以概括为:“Objenesis 优先,反射兜底”

  • 原因
    • 性能更高:Objenesis 通过直接操作字节码或 Unsafe 机制绕过构造函数,比反射调用构造函数更快。
    • 兼容性更强:它不需要目标类有无参构造函数,甚至可以实例化 final 类(在某些模式下)或构造函数为 private 的类。
      详见 ObjenesisCglibAopProxy
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {  
    Class<?> proxyClass = enhancer.createClass();  
    Object proxyInstance = null;  
    if (objenesis.isWorthTrying()) {  
        try {  
            proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());  
        } catch (Throwable ex) {  
            logger.debug("Unable to instantiate proxy using Objenesis, falling back to regular proxy construction", ex);  
        }  
    }  
  
    if (proxyInstance == null) {  
        try {  
            Constructor<?> ctor = this.constructorArgs != null ? proxyClass.getDeclaredConstructor(this.constructorArgTypes) : proxyClass.getDeclaredConstructor();  
            ReflectionUtils.makeAccessible(ctor);  
            proxyInstance = this.constructorArgs != null ? ctor.newInstance(this.constructorArgs) : ctor.newInstance();  
        } catch (Throwable ex) {  
            throw new AopConfigException("Unable to instantiate proxy using Objenesis, and regular proxy instantiation via default constructor fails as well", ex);  
        }  
    }  
  
    ((Factory)proxyInstance).setCallbacks(callbacks);  
    return proxyInstance;  
}

与反射的区别

特性 Objenesis (首选) 反射 (兜底)
实现原理 利用 JVM 底层 API (如 Unsafe/ReflectionFactory) 绕过构造函数。 基于 java.lang.reflect.Constructor 的标准反射机制。
性能 极高。直接分配内存,不执行构造函数逻辑。 较低。涉及 JNI 调用和安全检查。
构造函数调用 不调用。对象创建后,成员变量保持 JVM 默认值(null/0)。 调用。会执行构造函数中的代码逻辑。
适用场景 代理类(Proxy)、序列化框架、需要绕过复杂构造逻辑的场景。 普通的 Bean 实例化、需要执行构造函数初始化逻辑的场景。
风险 对象状态可能不完整(未初始化);在某些受限的 JVM 环境(如高版本 JDK 的安全管理器)下可能被禁止。 安全、标准,但受限于构造函数的可见性和参数要求。

对象何时初始化

对象实例属性的初始化被推迟到了 Spring 的**属性填充(Populate)初始化(Initialize)**阶段

  1. 依赖注入(DI):Spring 使用反射机制(BeanUtils / ReflectionUtils)扫描 @Autowired@Value 等注解,并通过 set 方法或直接 Field.set() 将依赖的对象或值注入进去。
  2. 显式赋值:如果需要特定的初始值,通常需要在 @PostConstruct 注解的方法中,或者 InitializingBeanafterPropertiesSet() 方法中手动赋值。

使用 CGLIB 时的注意事项

  1. 不能代理 final 类: 因为 CGLIB 是通过继承实现的,如果类被声明为 final,无法被继承,代理就会失败。
  2. 不能代理 final 方法: 同理,如果方法被声明为 final,子类无法重写该方法,因此无法织入切面逻辑。
  3. 构造函数调用: 由于是子类代理,创建代理对象时可能会调用目标类的构造函数,如果构造函数中有复杂的逻辑或副作用,需要留意。
  4. 依赖引入: 在 Spring Boot 中你通常不需要手动引入,因为 spring-boot-starter-aspectjspring-core-中已经包含了 CGLIB 和 ASM 的依赖。

使用示例

import org.junit.jupiter.api.Test;  
import org.springframework.cglib.proxy.Enhancer;  
import org.springframework.cglib.proxy.MethodInterceptor;  
import org.springframework.cglib.proxy.MethodProxy;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
public class ProxyTest {  
  
    @interface Author {  
        String name();  
  
        int age() default 1;  
    }  
  
    @Test  
    public void jdkProxyTest() {  
        UserService userService = new UserServiceImpl();  
        UserService proxy = (UserService) new JdkProxyHandler(userService).getProxy();  
        proxy.saveUser("张三");  
    }  
  
    @Test  
    public void cglibProxyTest() {  
        UserService userService = new UserServiceImpl();  
        UserService proxy = (UserService) new CglibProxyInterceptor(userService).getProxy();  
        proxy.saveUser("张三");  
    }  
}  
  
interface UserService {  
    void saveUser(String name);  
  
    void deleteUser(Long id);  
}  
  
class UserServiceImpl implements UserService {  
    @Override  
    public void saveUser(String name) {  
        System.out.println("业务逻辑:正在保存用户 -> " + name);  
    }  
  
    @Override  
    public void deleteUser(Long id) {  
        System.out.println("业务逻辑:正在删除用户 ID -> " + id);  
    }  
}  
  
class JdkProxyHandler implements InvocationHandler {  
    private Object target;  
  
    public JdkProxyHandler(Object target) {  
        this.target = target;  
    }  
  
    /**  
     * 获取代理对象  
     */  
    public Object getProxy() {  
        return Proxy.newProxyInstance(  
            target.getClass().getClassLoader(),      // 类加载器  
            target.getClass().getInterfaces(),       // 目标对象实现的接口  
            this );                                // 当前的 InvocationHandler      
    }  
  
    /**  
     * 拦截方法调用  
     */  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        System.out.println("[JDK代理] 方法执行前:日志开始...");  
  
        // 执行目标方法  
        Object result = method.invoke(target, args);  
  
        System.out.println("[JDK代理] 方法执行后:日志结束。");  
        return result;  
    }  
}  
  
class CglibProxyInterceptor implements MethodInterceptor {  
    // 被代理的对象  
    private Object target;  
  
    public CglibProxyInterceptor(Object target) {  
        this.target = target;  
    }  
  
    /**  
     * 获取代理对象  
     */  
    public Object getProxy() {  
        // 1. 创建增强器  
        Enhancer enhancer = new Enhancer();  
  
        // 2. 设置父类(即目标类)  
        enhancer.setSuperclass(target.getClass());  
  
        // 3. 设置回调(即拦截器)  
        enhancer.setCallback(this);  
  
        // 4. 创建子类(代理对象)  
        return enhancer.create();  
    }  
  
    /**  
     * 拦截方法调用  
     */  
    @Override  
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
        System.out.println("[CGLIB代理] 方法执行前:日志开始...");  
  
        // 注意:这里通常使用 proxy.invokeSuper(obj, args) 而不是反射  
        // proxy.invokeSuper 调用的是父类的方法,性能更好  
        Object result = proxy.invokeSuper(obj, args);  
  
        System.out.println("[CGLIB代理] 方法执行后:日志结束。");  
        return result;  
    }  
}
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐