@MapperScan 之使用注意点

现在很多项目持久层框架都会选择 mybatis ,原因就是 mybatis 配置灵活,使用简单,并且整合 Spring 之后,Dao 层声明的接口会由 spring 动态生成代理实现类。好的,重点来了,我在一次搭建 spring boot 项目的过程中,使用了 mybatis-plus 和 mybatis-plus-generator ,前者是 mybatis 增强版的封装,后者是其配套的代码生成器,这个生成器有点牛逼了,不仅 mapper、 entity 和 mapper.xml 都自动生成,还有 controller , IService, ServiceImpl 这些统统都生成了,一套代码下来就是可以使用的增删改查模块啊。当我想测试的时候,第一次启动报了错,说 service 需要依赖一个 Mapper ,期望至少找到一个 xxxMapper 类型的 bean ,噢,反应过来了,持久层的代码没扫描进容器,要解决这个问题,有两个方法,第一就是在生成的 mapper 接口类上加 @Mapper, 第二就是在springboot 启动类上加 @MapperScan 并指定扫描包范围,那为了省事儿,当然选择第二个方法,于是我这么写的 @MapperScan(“com.funtower”),那我的项目包结构是这样的:

│    
└─com  
    └─funtower  
        └─biz  
            └─youth  
                │  
				│--modulename  
					│  
					│--controller  
					│  
					│--service  
					│	│  
					│	│--impl   
					│  
					│--mapper  
					│  
					│--entity  

重点讲下 service 和 impl,前者里面是 IService ,是业务接口类,后者里面是接口对应的实现类,即 ServiceImpl,然后在 controller 里就像往常一样写

@AutoWired
private IxxxService xxxService;

我相信这种代码是不可能有问题的,因为我确定项目里就一个实现类,xxxServiceImpl,而 @AutoWired 注入的原则是先按类型,没有类型则按变量名(xxxService)到容器里去找 bean ,项目启动之后,卧槽,简直无情啊,报错了,还tm是这种看起来像是少了个配置,一下就能解决的,但是我花了半天都没解决的错误,先贴出来看看

2020-02-17 17:42:57.951 ERROR 13428 --- [  restartedMain] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field iScheTaskCfgService in com.funtower.biz.youth.task.controller.ScheTaskCfgController required a single bean, but 2 were found:
	- scheTaskCfgServiceImpl: defined in file [E:\FUNTOWER\project\wokspace\IDEA\USVSP\usvsp-parent\usvsp-biz\youth\target\classes\com\funtower\biz\youth\task\service\impl\ScheTaskCfgServiceImpl.class]
	- IScheTaskCfgService: defined in file [E:\FUNTOWER\project\wokspace\IDEA\USVSP\usvsp-parent\usvsp-biz\youth\target\classes\com\funtower\biz\youth\task\service\IScheTaskCfgService.class]

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

报错大致信息就是在 controller 里要注入一个 xxxService,期望能找到至少一个 IxxxService 类型的bean,但是却找到了两个,分别是 xxxSeriveImpl 和 IXXXSerice。卧槽,简直无情啊,当我是 SB 吗? IXXXService 明明是接口,他是抽象的,怎么会出现在容器里,我不信,这不是真的,我要打印出 spring 容器里所有的 bean ,于是我注释掉 controller 里的代码,然后在spring boot启动类里写下

ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
// 打印结果
...
IxxxService
xxxServiceImpl
...

事实再次打脸,还真有两个,这怎么可能,呵呵,我不信这个鬼报错,重启能解决很多问题的!我重启 IDEA,还是不行,我怀疑是 IDEA 的锅,于是打开 Eclipse ,还是不行,卧槽,重启电脑,还是不行,算了,别逃避了,就是我的问题,还是老老实实谷歌走一波,查了半天一直怀疑是代码自动生成的有问题,还有引入的依赖的问题,就没想到是 @MapperScan 扫描的问题。可能说到这,有过类似踩坑经历的同学肯定一下就反应过来了,扫描 com.funtower 的结果就是把这个包及以下的所有接口全部生成一个代理实现类丢到容器里去,并且他的名字 就和接口类名一模一样,所以我们使用@MapperScan的时候应该将包名精确到Dao层,在我的项目中应该@MapperScan(“com.xxx.**.mapper”),这两个星号是任意多级包名的通配符,一个星号只匹配一层包名。写到这问题水落石出,可以安心睡个觉了。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐