Micronaut Data @Repository注解引发Bean实例化异常问题
近期使用Micronaut框架开发数据库交互程序,因接触Gradle项目配置时间较短,初步怀疑问题与Gradle配置相关。
项目拆分为两个模块:
- main模块:存放Application启动类
- masterdata模块:存放全部业务代码
masterdata模块包含以下代码:
- 带
/test接口的Controller,接口仅返回字符串,代码如下:
package core.masterdata.module.controllers; import core.masterdata.module.entities.Person; import core.masterdata.module.repositories.PersonRepository; import core.masterdata.module.services.PersonService; import io.micronaut.core.version.annotation.Version; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import jakarta.inject.Inject; import java.util.Optional; import static core.masterdata.module.constants.EntitiesConstants.BASE_URL; @Controller(BASE_URL + "/person/") @Version("1") public class PersonController { @Inject PersonService personService; @Inject PersonRepository personRepository; @Get("test") public String getPerson(Long pId){ return "test"; } }
- Repository接口代码如下:
package core.masterdata.module.repositories; import core.masterdata.module.entities.Person; import io.micronaut.data.annotation.Repository; import io.micronaut.data.repository.CrudRepository; @Repository public interface PersonRepository extends CrudRepository<Person, Long> { }
- 实体类代码如下:
package core.masterdata.module.entities; import javax.persistence.*; @Entity @Table(name = "Person") public class Person { @Id @GeneratedValue @Column private Long id; @Column private String name; public Person(String name){ this.name = name; } public Person() { } public Long getId() { return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void setId(Long id) { this.id = id; } }
模块内另有一个标注@Singleton注解的空类。
项目可以正常构建编译,但在Controller或Service中注入Repository后调用对应API时会触发运行时错误。测试发现:
- 在Controller中注入上述
@Singleton标注的空类时运行无异常 - 如果将Repository注入到该空类、再将空类注入到Controller中,仍会触发相同错误
核心异常信息如下:
Unexpected error occurred: Error instantiating bean of type [core.masterdata.module.controllers.PersonController] Message: Multiple possible bean candidates found: [io.micronaut.aop.InterceptorRegistry, io.micronaut.aop.InterceptorRegistry] Path Taken: new PersonController() --> PersonController.personRepository io.micronaut.context.exceptions.BeanInstantiationException: Error instantiating bean of type [core.masterdata.module.controllers.PersonController] Caused by: io.micronaut.context.exceptions.NonUniqueBeanException: Multiple possible bean candidates found: [io.micronaut.aop.InterceptorRegistry, io.micronaut.aop.InterceptorRegistry]
该报错是典型的多模块Gradle配置错误导致的类重复加载问题:
业务模块masterdata没有使用官方标准的io.micronaut.library插件配置,错误地将Micronaut核心依赖(含InterceptorRegistry的aop模块)通过implementation/api直接声明,导致main启动模块、masterdata业务模块在构建时各自引入了一份完全相同的InterceptorRegistry类,Micronaut IoC容器启动扫描Bean时发现两个同类型的候选Bean,无法确定注入优先级,最终抛出非唯一Bean异常。
普通@Singleton注解的自定义类不需要AOP代理增强,所以注入时不会触发InterceptorRegistry的加载流程,不会报错;而@Repository标注的Repository接口需要Micronaut Data通过AOP实现代理类,实例化时必须注入InterceptorRegistry,就触发了重复Bean的错误。
另外代码中实体类使用javax.persistence.*注解,如果使用Micronaut 4及以上版本,需要统一替换为jakarta.persistence.*,但这个问题不会触发当前的重复Bean异常。
按以下步骤调整配置即可修复:
- 检查两个模块的
build.gradle插件配置:存放启动类的main模块应用io.micronaut.application插件,存放业务代码的masterdata模块仅应用io.micronaut.library插件,禁止在子模块中重复引入Micronaut核心BOM、micronaut-core、micronaut-aop等核心依赖,核心依赖版本统一通过根项目或主模块的Micronaut插件配置管理。 - masterdata子模块参考配置如下:
plugins { id "io.micronaut.library" } micronaut { // 版本号和main模块保持完全一致 version "对应Micronaut版本" } dependencies { // 仅声明模块需要的业务依赖,核心依赖由插件自动引入 annotationProcessor "io.micronaut.data:micronaut-data-processor" implementation "io.micronaut.data:micronaut-data-jdbc" // 其他业务依赖,比如数据库驱动、验证相关包等 }
- main模块通过
implementation project(':masterdata')正常引入业务模块即可,不需要额外配置扫描路径等参数,Micronaut会自动识别子模块内的Bean。 - 配置调整完成后执行
./gradlew clean清理全部旧构建缓存,再重新构建启动项目即可解决问题。 - 如果你使用的是Micronaut 4及以上版本,将实体类中所有
javax.persistence.*的导入替换为jakarta.persistence.*,避免后续JPA实体映射报错。
内容的提问来源于stack exchange,提问作者ale9600

