使用Launch4j Maven插件打包Java客户端遇InaccessibleObjectException的咨询
Java客户端打包exe后的反射访问异常处理
问题背景
我是Java新手,开发了一个连接WildFly实例的客户端应用,在IDE中运行正常。用akathist-launch4j-maven-plugin和maven-shade-plugin打包成.exe文件(不想用安装程序所以没选jpackage)后,运行到调用java.util.AbstractMap()时抛出异常:
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected java.util.AbstractMap() accessible: module java.base does not "opens java.util" to unnamed module @7e32c033
通过相关资料了解到,给JVM传--add-opens java.base/java.lang=ALL-UNNAMED可能解决问题,之前已经给shade插件加了排除过滤器处理签名jar的问题。现在想问两个问题:
a) 这种解决方式是否正确?
b) 若正确,如何修改POM文件添加该参数?
附原POM插件片段:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> <configuration> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> </excludes> </filter> </filters> <shadedArtifactAttached>true</shadedArtifactAttached> <shadedClassifierName>shaded</shadedClassifierName> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>de/cni/client/gui/pages/main/StartApplication</mainClass> </transformer> </transformers> </configuration> </plugin> <plugin> <groupId>com.akathist.maven.plugins.launch4j</groupId> <artifactId>launch4j-maven-plugin</artifactId> <version>2.4.1</version> <executions> <execution> <id>l4j-clui</id> <phase>package</phase> <goals> <goal>launch4j</goal> </goals> <configuration> <headerType>console</headerType> <jar>${project.build.directory}/${project.artifactId}-${project.version}-shaded.jar</jar> <outfile>${project.build.directory}/app-client.exe</outfile> <classPath> <mainClass>[..]/client/gui/pages/main/StartApplication</mainClass> <preCp>anything</preCp> </classPath> <jre> <path>${java.home}/bin</path> <minVersion>17.0.2</minVersion> <maxVersion></maxVersion> <requiresJdk>true</requiresJdk> <requires64Bit>true</requires64Bit> </jre> </configuration> </execution> </executions> </plugin>
补充:发布问题一小时后,我尝试在launch4j插件的<jre>节点中添加如下配置,程序能正常运行,但觉得有更优方案:
<opts> <opt>--add-opens java.base/java.util=ALL-UNNAMED</opt> <opt>--add-opens java.base/java.util.concurrent=ALL-UNNAMED</opt> </opts>
解决方案
a) 这种解决方式是否正确?
正确且必要。原因如下:
- Java 9及以后的模块系统(JPMS)默认限制了对JDK内部API的反射访问,目的是提升安全性和封装性。
- 用shade插件打包后的jar属于「未命名模块」(unnamed module),而
java.util.AbstractMap的protected构造方法被反射调用时,JPMS默认不允许未命名模块访问java.base模块的java.util包内部成员,因此抛出InaccessibleObjectException。 - 你后来调整的
--add-opens java.base/java.util=ALL-UNNAMED比最初看到的java.lang更精准,完全匹配错误信息中提到的包,是正确的参数。
b) 如何修改POM文件添加该参数?
最适合exe场景的方式就是你自己尝试的在launch4j插件的<jre>节点中添加<opts>配置,修改后的launch4j插件配置如下:
<plugin> <groupId>com.akathist.maven.plugins.launch4j</groupId> <artifactId>launch4j-maven-plugin</artifactId> <version>2.4.1</version> <executions> <execution> <id>l4j-clui</id> <phase>package</phase> <goals> <goal>launch4j</goal> </goals> <configuration> <headerType>console</headerType> <jar>${project.build.directory}/${project.artifactId}-${project.version}-shaded.jar</jar> <outfile>${project.build.directory}/app-client.exe</outfile> <classPath> <mainClass>de/cni/client/gui/pages/main/StartApplication</mainClass> <preCp>anything</preCp> </classPath> <jre> <path>${java.home}/bin</path> <minVersion>17.0.2</minVersion> <maxVersion></maxVersion> <requiresJdk>true</requiresJdk> <requires64Bit>true</requires64Bit> <!-- 添加JVM参数 --> <opts> <opt>--add-opens java.base/java.util=ALL-UNNAMED</opt> <opt>--add-opens java.base/java.util.concurrent=ALL-UNNAMED</opt> </opts> </jre> </configuration> </execution> </executions> </plugin>
另外,如果你希望直接运行shaded jar时也带这些参数,可以在maven-shade-plugin的ManifestResourceTransformer中添加manifestEntries配置JVM参数,但对于exe场景,launch4j的方式更直接,因为exe会自动把这些参数传给启动的JVM。
优化建议
虽然当前方法能解决问题,但可以进一步排查根源:
- 检查代码或依赖的WildFly客户端库是否在JPMS环境下没有适配,导致反射调用JDK内部API。
- 尝试升级WildFly客户端依赖的版本,很多主流库在Java 9+已经做了JPMS适配,可能不需要手动添加
--add-opens参数,这才是更优的长期解决方案。
内容的提问来源于stack exchange,提问作者derYak
相关产品推荐
相关产品推荐

