解决升级到JDK17后cglib报错Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass()
Posted Only丿阿海
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决升级到JDK17后cglib报错Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass()相关的知识,希望对你有一定的参考价值。
场景
在JDK17中引用seata作为分布式事务控制,项目启动失败,整体报文如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'globalTransactionScanner' defined in class path resource [io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.seata.spring.annotation.GlobalTransactionScanner]: Factory method 'globalTransactionScanner' threw exception; nested exception is java.lang.ExceptionInInitializerError
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:258) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:762) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:567) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.4.jar:2.7.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.4.jar:2.7.4]
at com.fbi.fvite.FViteApplication.main(FViteApplication.java:10) ~[classes/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.seata.spring.annotation.GlobalTransactionScanner]: Factory method 'globalTransactionScanner' threw exception; nested exception is java.lang.ExceptionInInitializerError
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.23.jar:5.3.23]
... 19 common frames omitted
Caused by: java.lang.ExceptionInInitializerError: null
at net.sf.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:166) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:144) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:116) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:108) ~[cglib-3.1.jar:na]
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:104) ~[cglib-3.1.jar:na]
at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:69) ~[cglib-3.1.jar:na]
at io.seata.config.ConfigurationCache.proxy(ConfigurationCache.java:104) ~[seata-all-1.5.2.jar:1.5.2]
at io.seata.config.ConfigurationFactory.buildConfiguration(ConfigurationFactory.java:136) ~[seata-all-1.5.2.jar:1.5.2]
at io.seata.config.ConfigurationFactory.getInstance(ConfigurationFactory.java:94) ~[seata-all-1.5.2.jar:1.5.2]
at io.seata.spring.annotation.GlobalTransactionScanner.<init>(GlobalTransactionScanner.java:105) ~[seata-all-1.5.2.jar:1.5.2]
at io.seata.spring.annotation.GlobalTransactionScanner.<init>(GlobalTransactionScanner.java:162) ~[seata-all-1.5.2.jar:1.5.2]
at io.seata.spring.boot.autoconfigure.SeataAutoConfiguration.globalTransactionScanner(SeataAutoConfiguration.java:83) ~[seata-spring-boot-starter-1.5.2.jar:1.5.2]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.23.jar:5.3.23]
... 20 common frames omitted
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @bcec361
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na]
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na]
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) ~[na:na]
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193) ~[na:na]
at net.sf.cglib.core.ReflectUtils$2.run(ReflectUtils.java:56) ~[cglib-3.1.jar:na]
at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) ~[na:na]
at net.sf.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:46) ~[cglib-3.1.jar:na]
... 39 common frames omitted
分析
- 这个错误是seata中引用的cglib库使用了不安全的API导致。
- 去cglib仓库发现最后一次更新是2019年,基本已不再维护,因此无法通过升级cglib解决;
- seata尚未发布修复此问题的版本,因此暂时需要拉取seata源码手动修复并编译。
(该文章发布日seata尚未发布修复版本,但或许当你看到这篇文章时已经发布修正此问题的正式版本了,具体请留意https://github.com/seata/seata/pull/4434, 若仍未修复请看下面的临时解决方案
)
考虑到使用cglib的开源库并不少,因此这篇文章以seata为例提供一个思路,可以解决所有因cglib导致的此类问题。
临时解决方案
- 拉取seata官方仓库
https://github.com/seata/seata
- 修改
all/pom.xml
和config/seata-config-core/pom.xml
, 修改cglib
的引用,使用bytebuddy
代替cglib
,seata项目中已经有了bytebuddy
的关联依赖,因此不需要重复指定版本号。
<dependency>
<!-- <groupId>cglib</groupId>
<artifactId>cglib</artifactId> -->
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</dependency>
- 修改
config/seata-config-core/src/main/java/io/seata/config/ConfigurationCache.java
的proxy
方法,改为bytebuddy
实现方式,代码如下
public Configuration proxy(Configuration originalConfiguration)
DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition<Configuration> intercept = new ByteBuddy()
.subclass(Configuration.class)
.method(ElementMatchers.any())
.intercept(InvocationHandlerAdapter.of((proxy, method, args) ->
if (method.getName().startsWith(METHOD_PREFIX)
&& !method.getName().equalsIgnoreCase(METHOD_LATEST_CONFIG))
String rawDataId = (String) args[0];
ObjectWrapper wrapper = CONFIG_CACHE.get(rawDataId);
ObjectWrapper.ConfigType type = ObjectWrapper.getTypeByName(method.getName().substring(METHOD_PREFIX.length()));
Object defaultValue = null;
if (args.length > 1 && method.getParameterTypes()[1].getSimpleName().equalsIgnoreCase(type.name()))
defaultValue = args[1];
if (null == wrapper || (null != defaultValue && !Objects.equals(defaultValue, wrapper.lastDefaultValue)))
Object result = method.invoke(originalConfiguration, args);
// The wrapper.data only exists in the cache when it is not null.
if (result != null)
wrapper = new ObjectWrapper(result, type, defaultValue);
CONFIG_CACHE.put(rawDataId, wrapper);
return wrapper == null ? null : wrapper.convertData(type);
return method.invoke(originalConfiguration, args);
));
try
return intercept.make().load(ClassLoader.getSystemClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded().newInstance();
catch (InstantiationException | IllegalAccessException e)
throw new RuntimeException(e);
- 为避免本地包和release版本发生冲突,最好手动修改一下seata的版本号(
build/pom.xml
)如下:
<!-- <revision>1.6.0-SNAPSHOT</revision> -->
<revision>1.6.0-SNAPSHOT.fixj17</revision>
- 编译、并安装到本地maven仓库
mvn clean install
- 回到你的项目,排除有问题的seata依赖,并引入刚刚编译的修复版
<!-- 分布式事务 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<!-- 排除有问题的依赖 -->
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加修复版 -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.6.0-SNAPSHOT.fixj17</version>
</dependency>
刷新maven依赖后,至此问题解决。这是一个解决思路,可以适用于cglib在高版本jdk中导致的此类问题。
另一种思路
除了以上方式外,spring官方也给出了修复方案,如果你使用了spring框架,可以使用spring的修正版cglib,也就是说你无需重新实现代码也可以解决此类问题。
https://github.com/spring-projects/spring-framework/tree/main/spring-core/src/main/java/org/springframework/cglib
使用这里的来代替cglib的api,它和cglib的api完全一致,无需重新实现。
- 但由于我这里是seata-config的一个核心包导致的问题,并没有引入spring,所以我选择用前者修复.
- 事实上seata的其他模块已经有使用spring的cglib来取代原版cglib的骚操作了,可以看一下这个类:
seata-spring-autoconfigure/seata-spring-autoconfigure-core/src/main/java/io/seata/spring/boot/autoconfigure/provider/SpringBootConfigurationProvider.java
。
以上是关于解决升级到JDK17后cglib报错Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass()的主要内容,如果未能解决你的问题,请参考以下文章
tomcat启动后报错Bad version number in .class file (unable to load class oracle.jdbc.OracleDriver)
eclipse升级Android SDK Tool版本到25.2.5后运行项目报错Unable to build: the file dx.jar was not loaded from the SD
升级jenkins之后无法启动 报错Unable to read /var/lib/jenkins/config.xml
Oracle SQL Developer报错:Unable to find a Java Virtual Machine解决办法