JDK 16 Spring Boot调用LevelDB报java.nio模块访问异常如何解决?
问题根因
你遇到的报错本质是使用的org.iq80.leveldb 0.9版本发布时间过早(2013年),早于Java模块化机制发布,内部通过反射调用了JDK内部API java.nio.DirectByteBuffer.cleaner(),而JDK 16开始默认对JDK内部API做强封装,不允许未授权的反射访问,因此抛出权限异常。
你之前添加module-info.java反而报错更多的原因是:添加该文件后你的应用会被识别为显式模块化应用,要求所有依赖都符合模块化规范,但你使用的Spring Boot 2.4.x、旧版leveldb等依赖都没有做模块化适配,因此会出现大量跨模块访问错误。普通Spring Boot应用不需要额外添加module-info.java,保持默认的未命名模块模式即可。
不使用JVM参数的解决方案
方案一:替换为兼容高版本JDK的RocksDB(推荐)
RocksDB是基于LevelDB优化的存储引擎,完全兼容LevelDB的核心API,且官方Java绑定已经适配了JDK 16+的模块化规则,不需要访问JDK内部API。
- 修改
pom.xml依赖,删除原有org.iq80.leveldb的两个依赖,添加RocksDB依赖:
<dependency> <groupId>org.rocksdb</groupId> <artifactId>rocksdbjni</artifactId> <version>8.8.1</version> </dependency>
- 调整
DatabaseConnection代码适配RocksDB API,逻辑改动极小:
package io.strata.node.stratanode.database; import org.rocksdb.Options; import org.rocksdb.RocksDB; import org.rocksdb.RocksDBException; public class DatabaseConnection { static { RocksDB.loadLibrary(); } private RocksDB database; public DatabaseConnection() { openDatabaseConnection("test"); } public boolean openDatabaseConnection(String filePath) { try (Options options = new Options().setCreateIfMissing(true)) { database = RocksDB.open(options, filePath); System.out.println(filePath + " has been created"); return true; } catch (RocksDBException e) { e.printStackTrace(); } System.out.println(filePath + " database could not be opened/created"); return false; } }
方案二:升级Spring Boot + 使用社区维护的LevelDB分支
如果需要保留原有LevelDB的代码逻辑,可以使用社区修复了高版本JDK兼容性的LevelDB分支,同时升级Spring Boot到适配JDK16的版本:
- 升级Spring Boot父依赖到2.7.x LTS版本,该版本原生支持JDK16运行:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.17</version> <relativePath/> </parent>
- 替换原有LevelDB依赖为社区修复版:
<!-- 移除原有org.iq80.leveldb的两个依赖后添加 --> <dependency> <groupId>io.github.kamildaron</groupId> <artifactId>leveldb</artifactId> <version>0.13</version> </dependency>
该分支已经移除了对DirectByteBuffer.cleaner()的反射调用,无需修改原有业务代码即可直接运行。
内容的提问来源于stack exchange,提问作者Imran
相关产品推荐
相关产品推荐

