You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移至Java21+SpringBoot3.2.0时Cassandra配置的私有字段访问问题

迁移Java21+SpringBoot3.2.0时Cassandra配置权限问题的安全解决方案

问题场景

从Java8+SpringBoot v1.x迁移至Java21+SpringBoot v3.2.0,配置Cassandra时遇到反射访问java.util.GregorianCalendar私有字段的权限错误。临时通过添加JVM参数--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED可解决,但该方案存在安全风险,需寻求合规的解决方式。

报错信息

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cassandraMappingContext' defined in class path resource [com/blabla/main/CassandraConfig.class]: Unable to make field private long java.util.GregorianCalendar.gregorianCutover accessible: module java.base does not "opens java.util" to unnamed module @61862a7f
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1775)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:224)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1321)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1282)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:484)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
at org.springframework.data.cassandra.config.AbstractSessionConfiguration.requireBeanOfType(AbstractSessionConfiguration.java:103)
at org.springframework.data.cassandra.config.AbstractCassandraConfiguration.cassandraConverter(AbstractCassandraConfiguration.java:76)
at com.blabla.main.CassandraConfig$$SpringCGLIB$$0.CGLIB$cassandraConverter$20(<generated>)
at com.blabla.main.CassandraConfig$$SpringCGLIB$$FastClass$$0.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
at com.blabla.main.CassandraConfig$$SpringCGLIB$$0.cassandraConverter(<generated>)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140)
142 common frames omitted
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private long java.util.GregorianCalendar.gregorianCutover accessible: module java.base does not "opens java.util" to unnamed module @61862a7f
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.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:315)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:183)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:177)
at org.springframework.util.ReflectionUtils.makeAccessible(ReflectionUtils.java:804)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:616)
at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:728)
at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:471)
at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:424)
at org.springframework.data.cassandra.core.mapping.CassandraMappingContext.addPersistentEntity(CassandraMappingContext.java:326)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.lambda$createAndRegisterProperty$3(AbstractMappingContext.java:671)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.createAndRegisterProperty(AbstractMappingContext.java:668)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:622)
at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:728)
at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:471)
at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:424)
at org.springframework.data.cassandra.core.mapping.CassandraMappingContext.addPersistentEntity(CassandraMappingContext.java:326)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.lambda$createAndRegisterProperty$3(AbstractMappingContext.java:671)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.createAndRegisterProperty(AbstractMappingContext.java:668)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:622)
at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:728)
at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:471)
at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:424)
at org.springframework.data.cassandra.core.mapping.CassandraMappingContext.addPersistentEntity(CassandraMappingContext.java:326)
at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:384)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.data.cassandra.CassandraManagedTypes.forEach(CassandraManagedTypes.java:81)
at org.springframework.data.mapping.context.AbstractMappingContext.initialize(AbstractMappingContext.java:563)
at org.springframework.data.cassandra.core.mapping.CassandraMappingContext.initialize(CassandraMappingContext.java:114)
at org.springframework.data.mapping.context.AbstractMappingContext.afterPropertiesSet(AbstractMappingContext.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1822)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771)
163 common frames omitted

当前Cassandra配置代码

@Configuration
@EnableCassandraRepositories(basePackages = "com.blabla")
public class CassandraConfig extends AbstractCassandraConfiguration {

  @Value("${cassandra.contactpoints}")
  private String contactPoints;

  @Value("${cassandra.port}")
  private int port;

  @Value("${cassandra.keyspace}")
  private String keySpace;

  @Value("${cassandra.basePackages}")
  private String basePackages;

  @Value("${cassandra.cluster}")
  private String clusterName;

  @Value("${cassandra.username}")
  private String username;
  
  @Value("${cassandra.consistency}")
  private String consistency;

  private String password;

  private static final Logger log = LoggerFactory.getLogger(CassandraConfig.class);

  @PostConstruct
  public void init(){
    // logic to get my password to connect with cassandra from another repo.
  }

  protected AuthProvider getAuthProvider() {
    return new PlainTextAuthProvider(username, password);
  }

  @Override
  protected String getKeyspaceName() {
    return keySpace;
  }

  @Override
  protected String getContactPoints() {
    return contactPoints;
  }

  @Override
  protected int getPort() {
    return port;
  }

  @Override
  public SchemaAction getSchemaAction() {
    return SchemaAction.CREATE_IF_NOT_EXISTS;
  }

  @Override
  public String[] getEntityBasePackages() {
    return new String[] {basePackages};
  }

  @Override
  protected String getClusterName() {
    return clusterName;
  }

  @Bean
  public QueryLogger queryLogger(Cluster cluster) {
    QueryLogger queryLogger = QueryLogger.builder().build();
    cluster.register(queryLogger);
    return queryLogger;
  }

  protected AddressTranslator getAddressTranslator() {
    return new CassandraAddressTranslator();
  }
  
  protected QueryOptions getQueryOptions() {
    QueryOptions queryOptions = new QueryOptions();
    queryOptions.setConsistencyLevel(ConsistencyLevel.valueOf(consistency));
    return queryOptions;
  }

}

使用的依赖

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-cassandra</artifactId>
    <version>4.3.3</version>
 </dependency>

安全合规解决方案

1. 升级Spring Data Cassandra版本

Spring Data Cassandra 4.3.x对Java模块系统的兼容性存在缺陷,建议升级到与SpringBoot3.2.0匹配的最新稳定版(SpringBoot3.2.0对应Spring Data Cassandra 4.4.x系列)。升级后框架会修复反射访问JDK内部类的问题:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-cassandra</artifactId>
    <version>4.4.1</version> <!-- 或更高兼容版本 -->
</dependency>

2. 替换实体类中的GregorianCalendar类型

将实体类中使用的GregorianCalendar替换为Java8+标准日期时间API类型(如LocalDateTime、ZonedDateTime或Instant),Spring Data Cassandra对这些类型有原生支持,无需反射访问JDK内部私有字段:

// 原字段
// private GregorianCalendar createTime;

// 替换后
private LocalDateTime createTime;

确保Cassandra表中对应字段类型为timestamp或datetime,保持类型匹配。

3. 自定义类型转换器(保留GregorianCalendar时)

若因业务需求必须保留GregorianCalendar,可自定义类型转换器,让框架通过转换逻辑处理类型映射,避免直接反射:

import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import java.time.Instant;
import java.util.GregorianCalendar;
import java.util.TimeZone;

@WritingConverter
public enum GregorianCalendarToInstantConverter implements Converter<GregorianCalendar, Instant> {
    INSTANCE;

    @Override
    public Instant convert(GregorianCalendar source) {
        return source.toInstant();
    }
}

@ReadingConverter
public enum InstantToGregorianCalendarConverter implements Converter<Instant, GregorianCalendar> {
    INSTANCE;

    @Override
    public GregorianCalendar convert(Instant source) {
        GregorianCalendar calendar = new GregorianCalendar();
        calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
        calendar.setTimeInMillis(source.toEpochMilli());
        return calendar;
    }
}

在CassandraConfig中注册转换器:

import org.springframework.data.cassandra.core.convert.CassandraSimpleTypeHolder;
import org.springframework.data.cassandra.core.convert.CustomConversions;
import java.util.Arrays;

@Override
public CustomConversions customConversions() {
    return CustomConversions.of(CassandraSimpleTypeHolder.DEFAULT, 
        Arrays.asList(GregorianCalendarToInstantConverter.INSTANCE, InstantToGregorianCalendarConverter.INSTANCE));
}

4. 缩小实体扫描范围

确认getEntityBasePackages()返回的包路径仅包含自身业务实体类,避免框架扫描到包含GregorianCalendar的第三方类或JDK内部类,减少不必要的反射操作。

内容的提问来源于stack exchange,提问作者Prakhar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:42:31