Java11的ClassCastException问题处理

现象

java.lang.reflect.InaccessibleObjectException

在jdk1.8下可以正常运行。切换成jdk11之后,运行时报如下的异常:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
java.lang.reflect.InaccessibleObjectException: Unable to make field final jdk.internal.loader.URLClassPath jdk.internal.loader.ClassLoaders$AppClassLoader.ucp accessible: module java.base does not "opens jdk.internal.loader" to unnamed module @bd2f5a9
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:340)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:280)
	at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176)
	at java.base/java.lang.reflect.Field.setAccessible(Field.java:170)
	at com.kutesmart.etl.util.JarLoaderUtil.initAddMethod(JarLoaderUtil.java:24)
	at com.kutesmart.etl.util.JarLoaderUtil.<clinit>(JarLoaderUtil.java:18)
	at com.kutesmart.etl.service.DataxService.getConnection(DataxService.java:358)
	at com.kutesmart.etl.service.DataxService.createTable(DataxService.java:273)
	at com.kutesmart.etl.service.DataxService.buildJson(DataxService.java:166)
	at com.kutesmart.etl.manager.ExecTimeTaskManage.execute(ExecTimeTaskManage.java:90)
	at com.kutesmart.etl.manager.ExecTimeTaskManage.exec2(ExecTimeTaskManage.java:49)
	at com.kutesmart.etl.manager.ExecTimeTaskManage.xxlJob(ExecTimeTaskManage.java:246)
	at com.kutesmart.etl.service.TaskJob.taskHandler(TaskJob.java:41)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.xxl.job.core.handler.impl.MethodJobHandler.execute(MethodJobHandler.java:31)
	at com.xxl.job.core.thread.JobThread$1.call(JobThread.java:143)
	at com.xxl.job.core.thread.JobThread$1.call(JobThread.java:136)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.lang.Thread.run(Thread.java:834)

原因

参考链接:https://learn.microsoft.com/zh-cn/java/openjdk/transition-from-java-8-to-java-11#classloader-cautions

在Java8中,可以将系统类加载程序强制转换为URLClassLoader。这通常有需要在运行时将类注入到classpath的应用程序和库完成。类加载程序层次结构在Java11中已更改。系统类加载程序(也称为应用程序类加载程序)现在是一个内部类。强制转换为URLClassLoader会在运行时引发ClassCastException。Java11无法通过API在运行时动态增强classpath,但可以通过反射来实现这一点,他会显示有关如何使用内部API的显著警告。

在Java11中,启动类加载程序只加载核心模块。如果创建一个具有null父项的类加载程序,则他可能找不到全部平台类。

在Java11中,需要在此类情况下传递ClassLoader.getPlatformClassLoader()而不是null作为父类加载程序。

解决

添加启动参数,在运行Java应用程序时,添加以下启动参数:

1
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED

由于我们的程序是用k8s部署启动的,所以在deployment中的command修改为以下内容:

1
        command: ["java", "--add-opens", "java.base/jdk.internal.loader=ALL-UNNAMED", "-jar", "/opt/cnbl-etl-0.0.1-SNAPSHOT.jar", "--spring.profiles.active=prod"]
Buy me a coffee
支付宝
微信
0%