Micronaut+GraalVM Native(AWS Lambda部署)集成MyBatis后GraalVM镜像运行时出现Javassist缺失错误
我之前在类似的技术栈组合下也踩过这个坑:本地JVM运行一切正常,但打包成GraalVM原生镜像部署到Lambda就抛出Cannot enable lazy loading because Javassist is not available的错误。核心原因是GraalVM原生镜像采用AOT(提前编译)模式,不支持运行时动态生成字节码的工具(比如Javassist),而MyBatis默认依赖Javassist来生成Mapper接口的动态代理实现。
下面是我验证有效的解决方案,分步骤说明:
1. 替换MyBatis的动态代理方式为JDK代理
MyBatis支持两种代理实现:Javassist(默认)和JDK动态代理。我们可以修改MyBatis配置,强制使用JDK代理,彻底摆脱对Javassist的依赖。
修改你的MybatisFactory类,在构建Configuration后添加JDK代理的配置:
@Factory class MybatisFactory(private val dataSource: DataSource) { @Singleton fun sqlSessionFactory(): SqlSessionFactory { val transactionFactory = ManagedTransactionFactory() val environment = Environment("micronaut", transactionFactory, dataSource) val configuration = Configuration(environment) // 关键:强制切换为JDK动态代理,替代默认的Javassist configuration.useJdkProxy = true configuration.addMappers(BASE_PACKAGE) return SqlSessionFactoryBuilder().build(configuration) } companion object { private const val BASE_PACKAGE = "com.foo.bar" } }
2. 配置GraalVM原生镜像的元数据(反射/代理)
GraalVM提前编译时无法自动识别MyBatis Mapper接口的反射调用和代理需求,我们需要手动添加元数据配置,让GraalVM知道这些类需要被反射访问、允许生成代理。
在项目src/main/resources/META-INF/native-image/com.foo.bar/reflect-config.json(路径根据你的项目groupId调整)中添加以下配置(以MyMapper为例):
[ { "name": "com.foo.bar.MyMapper", "allDeclaredMethods": true, "allPublicMethods": true, "methods": [ { "name": "findById", "parameterTypes": ["int"] }, { "name": "insert", "parameterTypes": ["com.foo.bar.MyModel"] } ] }, { "name": "com.foo.bar.MyModel", "allDeclaredFields": true, "allPublicFields": true, "allDeclaredMethods": true, "allPublicMethods": true } ]
这个配置告诉GraalVM:MyMapper接口的方法需要被反射调用,MyModel实体类的字段和方法需要被MyBatis反射操作。如果有多个Mapper接口,需要为每个Mapper添加类似配置,也可以借助Micronaut的原生镜像插件自动扫描生成部分元数据。
3. 适配事务与Session的绑定逻辑
你使用了ManagedTransactionFactory配合Spring-TX的@Transactional,在GraalVM下需要确保事务上下文能正确绑定到MyBatis的Session,避免每次调用都新开独立Session导致事务失效。调整MapperExecutor的逻辑:
@Singleton class MapperExecutor(private val sqlSessionFactory: SqlSessionFactory) { fun <T, R> execute(mapperClass: Class<T>, block: (T) -> R): R { // 优先从事务上下文获取绑定的Session,无事务时再新开Session val session = TransactionSynchronizationManager.getResource(sqlSessionFactory) as? SqlSession ?: sqlSessionFactory.openSession() return try { val mapper = session.getMapper(mapperClass) block(mapper) } finally { // 仅当Session未绑定事务时才手动关闭 if (!TransactionSynchronizationManager.isActualTransactionActive()) { session.close() } } } }
4. 依赖检查与调整
确保项目依赖中没有显式引入Javassist(如有可直接移除),同时添加Micronaut和MyBatis的GraalVM支持依赖,以Gradle为例:
implementation("io.micronaut.sql:micronaut-jdbc-hikari") implementation("org.mybatis:mybatis:3.5.13") implementation("org.springframework:spring-tx:6.0.15") // Micronaut原生镜像运行时支持 runtimeOnly("io.micronaut:micronaut-runtime-native-image")
最后验证
修改完成后重新构建GraalVM原生镜像:
# Gradle项目 ./gradlew nativeCompile # Maven项目 ./mvnw package -Dpackaging=native-image
部署到AWS Lambda测试后,Javassist相关的错误应该就会消失了。
内容来源于stack exchange

