GraalVM Native Image运行失败:EclipseStore导致无法访问Collections$SetFromMap
GraalVM构建Spring Boot Native Image时EclipseStore引发的反射访问异常问题
环境信息
- GraalVM版本:21.0.5(适配Java 21)
- Java版本:Java 21
- Spring Boot版本:3.3.5
- 操作系统:Sonoma 14.6.1
问题详情
引入EclipseStore 2.0.0依赖后,构建Native Image并运行时出现以下反射访问异常:
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field final java.util.Map java.util.Collections$SetFromMap.m accessible: module java.base does not "opens java.util" to unnamed module @60a7e509 at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:391) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:367) at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:183) at java.base/java.lang.reflect.Field.setAccessible(Field.java:177) at org.eclipse.serializer.reflect.XReflect.setAccessible(XReflect.java:110) ...
该异常表明应用通过反射访问JDK内部类时,被Java模块系统阻止。
已尝试的解决方案
- 构建时添加
--add-opens参数:在pom.xml中配置--add-opens=java.base/java.util=ALL-UNNAMED; - 运行时传递
--add-opens参数:通过-H:AdditionalRuntimeOptions将参数传入运行时; - 注册反射提示:在RuntimeHints.java中注册
java.util.Collections$SetFromMap类及所有成员; - 使用反射配置文件:创建reflect-config.json配置反射规则。
以上尝试均未解决问题。
疑问
- 如何正确配置GraalVM与Java模块系统,允许反射访问
java.util.Collections$SetFromMap? - 该问题是否由EclipseStore与Java 21/GraalVM 21.0.5不兼容导致?
- 是否存在不依赖JDK内部类的替代解决方案?
相关代码与配置
EclipseAccountAdapter类代码
public class EclipseAccountAdapter { private final EmbeddedStorageManager storageManager; private static final Logger log = LoggerFactory.getLogger(EclipseAccountAdapter.class); private final ApplicationContext context; public EclipseAccountAdapter(EmbeddedStorageManager storageManager, ApplicationContext context) { this.storageManager = storageManager; this.context = context; initializeRoot(); } private void initializeRoot() { if (storageManager.root() == null) { log.info("Initializing root."); storageManager.setRoot(new DataRoot()); storageManager.storeRoot(); } } @Write public int save(Account account, StorerType storerType) { getRoot().accounts().add(account); store(getRoot().accounts().usernameToAccount(), storerType); return getRoot().accounts().all().size(); } @Read public List<Account> findAll() { return new ArrayList<>(getRoot().accounts().all()); } private DataRoot getRoot() { return (DataRoot) storageManager.root(); } private void store(final Object object, StorerType storerType) { try { switch (storerType) { case LAZY: { Storer lazyStorer = storageManager.createLazyStorer(); lazyStorer.store(object); lazyStorer.commit(); break; } case EAGER: { Storer eagerStorer = storageManager.createEagerStorer(); eagerStorer.store(object); eagerStorer.commit(); } default: storageManager.store(object); } } catch (final Throwable t) { onStorageFailure(t); } } private void onStorageFailure(final Throwable t) { if (storageManager != null && storageManager.isRunning()) { try { log.error("Storage error! Shutting down storage...", t); SpringApplication.exit(context, () -> 0); } catch (final Throwable throwable) { log.error(throwable.getMessage(), throwable); } } DataRoot root = getRoot(); if (root != null) { root.clear(); } } }
pom.xml片段
<dependencies> <!-- Spring Boot and GraalVM dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.eclipse.store</groupId> <artifactId>storage-embedded</artifactId> <version>2.0.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> </plugin> </plugins> </build>
完整堆栈信息
2024-10-27T08:01:30.632-05:00 INFO 19610 --- [nativeImageTest] [ main] d.n.nativeImageTest.ServiceConfig : Initializing EmbeddedStorageManager... 2024-10-27T08:01:30.634-05:00 WARN 19610 --- [nativeImageTest] [ main] w.s.c.ServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'accountController': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'eclipseAccountAdapter': Unsatisfied dependency expressed through method 'eclipseAccountAdapter' parameter 0: Error creating bean with name 'injectStorageTest': Instantiation of supplied bean failed 2024-10-27T08:01:30.634-05:00 INFO 19610 --- [nativeImageTest] [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] Application run failed org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'accountController': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'eclipseAccountAdapter': Unsatisfied dependency expressed through method 'eclipseAccountAdapter' parameter 0: Error creating bean with name 'injectStorageTest': Instantiation of supplied bean failed Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'eclipseAccountAdapter': Unsatisfied dependency expressed through method 'eclipseAccountAdapter' parameter 0: Error creating bean with name 'injectStorageTest': Instantiation of supplied bean failed Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'injectStorageTest': Instantiation of supplied bean failed Caused by: org.eclipse.serializer.exceptions.NoSuchNestedClassRuntimeException: No nested class java.util.Collections$SetFromMap found in class java.util.Collections at org.eclipse.serializer.reflect.XReflect.getDeclaredNestedClass(XReflect.java:416) at org.eclipse.serializer.persistence.binary.java.util.BinaryHandlerSetFromMap.New(BinaryHandlerSetFromMap.java:39)
内容的提问来源于stack exchange,提问作者Nathan Lively
相关产品推荐
相关产品推荐

