bigywater.tech
Spring · 23

CGLIB 代理的限制:final、private 与构造器被调用几次

CGLIB 代理有哪些不能做的事?final 方法为什么读到 null?构造器会不会被调用两次?final 类能不能被代理?这篇在 Spring Framework 7.0.9 上逐项验证。

locked:null
通过 CGLIB 代理调用 final 方法时读到的字段值(目标对象里是真名)
1 对 2
默认(Objenesis)与 -Dspring.objenesis.ignore=true 下,构造器被调用的次数
不能继承
final 类被切面匹配时的错误:Cannot subclass final class

各种方法

实测 · 本机执行输出
  目标对象构造时 name="真名"。通过代理调用:
    open()(普通 public 方法)→ open:真名  [切面拦截 1 次:[open]]
    locked()(final 方法)→ locked:null  [切面拦截 0 次]
    callHidden()(public 方法,里面调 private hidden())→ hidden:真名  [切面拦截 1 次:[callHidden]]
    static stat() → static  [切面拦截 0 次]
    Svc.ctor(构造器被调用的次数)= 1;Bean 是代理?true

final 方法不被拦截且在代理自身上执行;private 和 static 方法不单独拦截。

构造器

实测 · 本机执行输出
  有 public 无参构造器的 Plain,变成代理后构造器被调用了 1 次;isAopProxy = true;spring.objenesis.ignore = (没设)
  有 public 无参构造器的 Plain,变成代理后构造器被调用了 2 次;isAopProxy = true;spring.objenesis.ignore = true

默认靠 Objenesis 不调用构造器,关掉后会调用两次。

只有带参构造器

实测 · 本机执行输出
  只有带参构造器的 Svc 变成代理:成功,构造器被调用 1 次;spring.objenesis.ignore = (没设)
  LOG WARN AnnotationConfigApplicationContext: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'svc' defined in demo.Main$C1: Unexpected AOP exception
  只有带参构造器的 Svc 变成代理:BeanCreationException ← NoSuchMethodException: demo.Main$Svc$$SpringCGLIB$$0.<init>()

能被代理是靠 Objenesis;关掉后找不到无参构造器就失败。

final 类

实测 · 本机执行输出
  LOG WARN AnnotationConfigApplicationContext: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fin' defined in demo.Main$C3: Could not generate CGLIB subclass of class demo.Main$Fin: Common causes of this problem include using a final class or a non-visible class
  final 类 Fin 被切面匹配:BeanCreationException ← IllegalArgumentException: Cannot subclass final class demo.Main$Fin

不能继承,无法代理。

分步演示

CGLIB 代理的限制
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9 + AspectJ 注解,proxyTargetClass = true;spring.objenesis.ignore 通过 JVM 系统属性设置
    • 没有验证:Spring 在 final 方法上是否打印警告(没有看到 WARN 日志);Java 记录(record)与 sealed 类的代理;protected 方法与不同包下包可见方法的拦截;Kotlin 默认 final 类的 allopen 插件;GraalVM native image 下的 CGLIB 限制
    京ICP备2026067502号-1京公网安备11010502063487号