手动配置Spring Data R2DBC遇无法访问字段错误求助
问题描述
应用需要连接两个数据库,因此手动配置R2DBC。单数据库自动配置时仓库可正常工作,但手动配置创建仓库实现时报错:
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.String java.net.URL.protocol accessible: module java.base does not "opens java.net" to unnamed module @69a61a00
实体包含类型为URL的属性,配置代码如下:
@Configuration @EnableR2dbcRepositories( entityOperationsRef = "postgresR2dbcEntityOperations", basePackages = ["com.acme.somepackage"], ) @EnableR2dbcAuditing class R2DBCConfiguration { @Bean @ConfigurationProperties("spring.r2dbc.postgres") fun postgresR2dbcProperties() = R2dbcProperties() @Bean fun postgresConnectionFactory( @Qualifier("postgresR2dbcProperties") props: R2dbcProperties ): ConnectionFactory { val host = ConnectionFactoryOptions .parse(props.url) .getRequiredValue(Option.valueOf<String>("host")) as String return PostgresqlConnectionConfiguration .builder() .host(host) .username(props.username) .password(props.password) .schema("myschema") .build() .let { PostgresqlConnectionFactory(it) } } @Bean fun postgresR2dbcEntityOperations( @Qualifier("postgresConnectionFactory") connectionFactory: ConnectionFactory, ): R2dbcEntityOperations { val mappingContext = R2dbcMappingContext() val dialect = DialectResolver.getDialect(connectionFactory) val converters = dialect.converters.toMutableList() converters.addAll(R2dbcCustomConversions.STORE_CONVERTERS) val storeConversions = StoreConversions.of(dialect.getSimpleTypeHolder(), converters) val customConverters = listOf( // customer converters ) val customConversions = R2dbcCustomConversions(storeConversions, customConverters) val mappingConverter = MappingR2dbcConverter(mappingContext, customConversions) val strategy = DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE, mappingConverter) val client = DatabaseClient.builder() .connectionFactory(connectionFactory) .bindMarkers(PostgresDialect.INSTANCE.bindMarkersFactory) .build() return R2dbcEntityTemplate(client, strategy) } }
完整堆栈信息:
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.String java.net.URL.protocol accessible: module java.base does not "opens java.net" to unnamed module @69a61a00 at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178) at java.base/java.lang.reflect.Field.setAccessible(Field.java:172) at org.springframework.util.ReflectionUtils.makeAccessible(ReflectionUtils.java:779) at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:536) at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:703) at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:422) at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:379) at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.lambda$createAndRegisterProperty$3(AbstractMappingContext.java:591) at java.base/java.lang.Iterable.forEach(Iterable.java:75) at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.createAndRegisterProperty(AbstractMappingContext.java:588) at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:542) at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:703) at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:422) at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:379) at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:280) at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:206) at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:92) at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$6(RepositoryFactoryBeanSupport.java:282) at java.base/java.util.Optional.ifPresent(Optional.java:178) at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:282) at org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactoryBean.afterPropertiesSet(R2dbcRepositoryFactoryBean.java:159)
请问自动配置中存在哪些我未实现的配置可以解决该问题?
解决方案
问题根源是手动配置时缺少了Spring R2DBC自动配置默认处理的URL类型映射逻辑,导致Spring Data尝试反射访问URL的私有字段触发模块访问限制。自动配置帮你做了这些关键配置,你需要手动补上:
- 添加URL与String的默认转换器
Spring R2DBC自动配置会默认注册UrlToStringConverter和StringToUrlConverter,这两个转换器能让URL类型直接与数据库字符串字段映射,避免Spring Data去解析URL私有属性。把它们加入自定义转换器列表:
import org.springframework.data.convert.StringToUrlConverter import org.springframework.data.convert.UrlToStringConverter // ... val customConverters = listOf( UrlToStringConverter(), StringToUrlConverter() ) // ...
- 正确初始化R2dbcMappingContext
自动配置会为R2dbcMappingContext设置正确的类型持有者,确保类型识别逻辑正常。修改映射上下文的初始化代码:
val mappingContext = R2dbcMappingContext().apply { setSimpleTypeHolder(customConversions.simpleTypeHolder) }
- 使用动态解析的方言而非硬编码
你的代码硬编码了PostgresDialect.INSTANCE,但自动配置会根据连接工厂动态获取方言,确保兼容性。修改数据访问策略的初始化:
val strategy = DefaultReactiveDataAccessStrategy(dialect, mappingConverter)
把这些修改整合到postgresR2dbcEntityOperations方法中,就能和自动配置的行为保持一致,解决反射访问URL私有字段的问题。
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

