-
在使用Graalvm打包一个项目,在运行的时候出现了如下的报错:
Exception in thread "main" java.lang.ExceptionInInitializerError at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:290) at java.lang.Class.ensureInitialized(DynamicHub.java:467) at okhttp3.OkHttpClient.<clinit>(OkHttpClient.java:127) at com.oracle.svm.core.hub.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:350) at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:270) at java.lang.Class.ensureInitialized(DynamicHub.java:467) at okhttp3.OkHttpClient$Builder.<init>(OkHttpClient.java:475) at io.fabric8.kubernetes.client.utils.HttpClientUtils.createHttpClient(HttpClientUtils.java:73) at io.fabric8.kubernetes.client.utils.HttpClientUtils.createHttpClient(HttpClientUtils.java:64) at io.fabric8.kubernetes.client.BaseClient.<init>(BaseClient.java:51) at io.fabric8.kubernetes.client.BaseClient.<init>(BaseClient.java:43) at io.fabric8.kubernetes.client.DefaultKubernetesClient.<init>(DefaultKubernetesClient.java:89) at ch.frankel.kubernetes.extend.Sidecar.main(Sidecar.java:13) Caused by: java.nio.charset.UnsupportedCharsetException: UTF-32BE at java.nio.charset.Charset.forName(Charset.java:531) at okhttp3.internal.Util.<clinit>(Util.java:75) at com.oracle.svm.core.hub.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:350) at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:270)
-
打包命令为:
native-image -jar simple-rpc-server.jar -H:+ReportExceptionStackTraces
-
从堆栈可以看出,有问题的代码位于:
public final class Util { private static final ByteString UTF_8_BOM = ByteString.decodeHex("efbbbf"); private static final ByteString UTF_16_BE_BOM = ByteString.decodeHex("feff"); private static final ByteString UTF_16_LE_BOM = ByteString.decodeHex("fffe"); private static final ByteString UTF_32_BE_BOM = ByteString.decodeHex("0000ffff"); private static final ByteString UTF_32_LE_BOM = ByteString.decodeHex("ffff0000"); public static final Charset UTF_8 = Charset.forName("UTF-8"); public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); private static final Charset UTF_16_BE = Charset.forName("UTF-16BE"); private static final Charset UTF_16_LE = Charset.forName("UTF-16LE"); private static final Charset UTF_32_BE = Charset.forName("UTF-32BE"); private static final Charset UTF_32_LE = Charset.forName("UTF-32LE"); public static Charset bomAwareCharset(BufferedSource source, Charset charset) throws IOException { if (source.rangeEquals(0, UTF_8_BOM)) { source.skip(UTF_8_BOM.size()); return UTF_8; } if (source.rangeEquals(0, UTF_16_BE_BOM)) { source.skip(UTF_16_BE_BOM.size()); return UTF_16_BE; } if (source.rangeEquals(0, UTF_16_LE_BOM)) { source.skip(UTF_16_LE_BOM.size()); return UTF_16_LE; } if (source.rangeEquals(0, UTF_32_BE_BOM)) { source.skip(UTF_32_BE_BOM.size()); return UTF_32_BE; } if (source.rangeEquals(0, UTF_32_LE_BOM)) { source.skip(UTF_32_LE_BOM.size()); return UTF_32_LE; } return charset; } }
-
打包后的可执行文件中,在加载类的时候,每个对应的
Charset
运行自己的Charset.forName()
,在操作系统中,没有对应的字符集。于是报错了。 -
此问题在Graalvm的github上也有提过Issues
解决方案
编译时添加所有字符集
- 在打包命令中添加参数
-H:+AddAllCharsets
- 缺点:会导致打包后的文件更大一点。
修复库
- 下载库的代码(如OkHttp)然后干掉这部分问题代码,自己打包构建,Maven引入。
使用substratevm
-
注:关于substratevm这块没有找到官方文档,以下大部分内容是直接谷歌来的。
-
项目中添加依赖:
<dependency> <groupId>com.oracle.substratevm</groupId> <artifactId>svm</artifactId> <version>19.2.1</version> <scope>provided</scope> </dependency>
-
编写替换的代码:
@TargetClass(Util.class) public final class okhttp3_internal_Util { @Alias private static ByteString UTF_8_BOM; @Alias private static ByteString UTF_16_BE_BOM; @Alias private static ByteString UTF_16_LE_BOM; @Alias public static Charset UTF_8; @Alias private static Charset UTF_16_BE; @Alias private static Charset UTF_16_LE; @Substitute public static Charset bomAwareCharset(BufferedSource source, Charset charset) throws IOException { if (source.rangeEquals(0, UTF_8_BOM)) { source.skip(UTF_8_BOM.size()); return UTF_8; } if (source.rangeEquals(0, UTF_16_BE_BOM)) { source.skip(UTF_16_BE_BOM.size()); return UTF_16_BE; } if (source.rangeEquals(0, UTF_16_LE_BOM)) { source.skip(UTF_16_LE_BOM.size()); return UTF_16_LE; } return charset; } }
- 其中:
- @TargetClass:被这个注解标注的类会在NativeImage编译期间替换方法执行的逻辑而无需改变原有代码
- @Substitute:被这个注解标注的方法会被替换。
- @Alias:被这个注解标注的变量会被替换。
- 其中:
-
添加配置文件:
[ { "annotatedClass": "用于替换的类的全限定类名", "originalClass": "需要被替换的类的全限定类名", "methods": [ { "annotatedName": "方法名", "substitute": true } ] } ]
-
然后native-image时指定该文件:
-H:SubstitutionFiles=xxx.json
阅读量
loading...