在升级到 JDK 17 后,你可能会遇到如下错误:
Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass()
问题详细描述
这个错误通常发生在使用 CGLIB(一个常用的字节码生成库)时。CGLIB 依赖于 Java 的反射机制来动态生成和操作类。当 CGLIB 尝试通过反射调用 ClassLoader.defineClass()
方法时,由于 JDK 17 引入了模块系统及更严格的访问控制策略,导致这个调用被拒绝。
关键原因
- 反射限制:JDK 17 增强了对模块和反射的限制,特别是在
java.base
模块中,许多类和方法的访问权限变得更严格。 - CGLIB 实现方式:CGLIB 使用字节码操作来动态创建子类,如果无法访问
defineClass
,则无法完成这一过程。
错误场景
- 当 CGLIB 尝试使用反射生成代理类时,会通过
ClassLoader
的defineClass
方法来加载生成的字节码。 - 如果此时 JVM 认为该方法不可访问,就会抛出上述错误。
解决方案
2、更新 CGLIB: 确保你使用的 CGLIB 版本与 JDK 17 兼容。推荐版本是 3.3.0 及以上,具体依赖示例如下:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version> <!-- 或更高版本 -->
</dependency>
2、JVM 参数调整: 如果更新版本后问题依旧,可以通过 JVM 参数来放宽反射限制:
--add-opens java.base/java.lang=ALL-UNNAMED
-
这个参数允许未命名模块访问
java.lang
包中的所有类,解决了访问问题。 -
替代方案: 考虑使用其他字节码生成库,例如 ByteBuddy,它对 JDK 17 的支持更好,或使用 Spring 的 AOP 功能来替代 CGLIB。
总结
在迁移到 JDK 17 时,确保所有使用的库与新版本兼容至关重要。通过更新 CGLIB 或调整 JVM 参数,如果以上方式都不行,那就不要用了,因为cglib官方也不再进行维护了。