java notfiy的用法_mybatis(1)- Springboot中@Mapper与@MapperScan规范使用

结论

[auto-configuration package  ]默认是启动类包路径。

@Mapper &  @MapperScan 混用也不会重复扫描。

单独@Mapper能正常启动是因为:MybatisAutoConfiguration的静态内部类MapperScannerRegistrarNotFoundConfiguration装配的AutoConfiguredMapperScannerRegistrar会扫描启动类路径包下带@Mapper的类。

不用给SqlSessionTemplate声明@bean,除非有特殊需要将SqlSessionTemplate归于容器管理。

否则将导致:MapperFactoryBean先执行父类SqlSessionDaoSupport.setSqlSessionFactory方法, 会新建一个SqlSessionTemplate; 但被后执行SqlSessionDaoSupport.setSqlSessionTemplate方法注入托管于容器的SqlSessionTemplate实例 覆盖。

正确的使用方式有2种

@Mapper + @ImportAutoConfiguration(MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class) + @SpringBootApplication(exclude = { MybatisAutoConfiguration.class})

(表示只要 MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class)

@MapperScan({ "com.noob.giant.dao" }) + @SpringBootApplication(exclude = { MybatisAutoConfiguration.class })

(package的路径要尽量精确到DAO层。也可以在@MapperScan 配置 annotationClass 来指定扫描的注解类型 或者 markerInterface 指定接口. 源码详见:org.mybatis.spring.annotation.MapperScannerRegistrar)

分析

MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar在执行过程中会扫描auto-configuration package路径下带@Mapper注解的类

MybatisAutoConfiguration的javadoc描述:

If {@link org.mybatis.spring.annotation.MapperScan} is used, or a  configuration file is specified as a property, those will be considered, otherwise this auto-configuration will attempt to register mappers based on  the interface definitions in or under the root auto-configuration package.

如果@MapperScan 或者特定的资源文件被加载,那就要慎重考虑是否使用了。此类会尝试注册auto-configuration package路径下定义的接口为mappers

比对下MapperScannerConfigurer源码、@MapperScanimport的MapperScannerRegistrar的源码,发现与AutoConfiguredMapperScannerRegistrar都是创建了ClassPathMapperScanner并确定扫描路径!

源码分析 详见:​​​​​mybatis的MapperScan到底做了什么

测试1

不使用@MapperScan和@Mapper:

public interface DecisionAdjustApplyMapper {

DecisionAdjustApply selectUnqiue(@Param("applyNo") String applyNo);

}

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;

import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;

import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

import org.springframework.cloud.netflix.feign.EnableFeignClients;

import org.springframework.context.annotation.EnableAspectJAutoProxy;

import org.springframework.context.annotation.ImportResource;

import org.springframework.scheduling.annotation.EnableAsync;

import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableDiscoveryClient

@EnableFeignClients

@EnableConfigurationProperties // 配合@ConfigurationProperties 合用

@EnableTransactionManagement(proxyTargetClass = true) // 启注解事务管理,等同于xml配置方式的

@EnableAsync //开启异步调用

@EnableAspectJAutoProxy //开启自定义切面

@ImportResource("classpath:dependence.xml")

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class})

//@MapperScan("com.noob.giant.dao")

public class GiantApplication {

public static void main(String[] args) {

//System.setProperty("DEPLOY_ENV", "test");

SpringApplication.run(GiantApplication.class, args);

}

}

结果:抛出异常 , Mapper对象无法注入容器!

Description:

A component required a bean of type 'com.noob.giant.dao.DecisionAdjustApplyMapper' that could not be found.

Action:

Consider defining a bean of type 'com.noob.giant.dao.DecisionAdjustApplyMapper' in your configuration.

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.noob.giant.dao.DecisionAdjustApplyMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}

at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1493)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)

at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)

at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:518)

at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:496)

at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:627)

at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)

at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)

at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:318)

... 54 common frames omitted

后续测试发现:

单独  在Mapper类上增加@Mapper  或   在启动类上增加“@MapperScan("com.noob.giant.dao")”   都能启动注入成功。

SqlSessionTemplate 被处理了2次?

在对每个Mapper对象进行beanWrapper过程中:

146827344887adf4f469d1dbe397af8a.png

创建MapperFactoryBean(extends SqlSessionDaoSupport)对象时,会先执行SqlSessionDaoSupport.setSqlSessionFactory方法根据传入的SqlSessionFactory (实际对象是DefaultSqlSessionFactory)创建一个SqlSessionTemplate;

而后被再执行SqlSessionDaoSupport.setSqlSessionTemplate方法又注入了一个SqlSessionTemplate来覆盖原有的。

跟踪执行断点发现:

29464264d4d58efb649eee0ee258699d.png

在AbstractAutowireCapableBeanFactory.populateBean方法中:入参对象RootBeanDefinition的PropertyValues初始值正常:

15ce981a35d3b4a39156ffdee55250a2.png

处理后最终得到的PropertyValues对象中却含SqlSessionFactory、SqlSessionTemplate !

f98a213c00883c59d7ecdb43e476701e.png

发现:

MapperFactoryBean两方法中的SqlSessionTemplate 实例 内容大致一样,使用相同的构造方法:new SqlSessionTemplate(sqlSessionFactory)。

各个不同Mapper通过setSqlSessionTemplate方法注入的SqlSessionTemplate 实例是同一个对象,且 setSqlSessionFactory方法的入参SqlSessionFactory实例也是同一个对象。

所以推测:

sqlSessionTemplate在另外的地方被初始化托管在Spring容器中,被当做自动注入的属性了。

测试2 - 论证AutoConfiguredMapperScannerRegistrar的作用

经查验发现:

在运行环境中引用的mybatis-spring-boot-autoconfigure.jar中有一个MybatisAutoConfiguration自动装载了sqlSessionTemplate托管于容器,且提供了扫描@Mapper的处理类AutoConfiguredMapperScannerRegistrar。

org.mybatis.spring.boot

mybatis-spring-boot

1.3.2

mybatis-spring-boot-autoconfigure

mybatis-spring-boot-autoconfigure

与其他无此jar引用仍旧正常启动且只会执行setSqlSessionFactory方法的项目比对后, 发现没有MybatisAutoConfiguration加载也能启动,但多了个配置:

第一次

@Mapper + 启动类排除自动装载: @SpringBootApplication(exclude = { MybatisAutoConfiguration.class })

结果:失败!无法正常注入Mapper对象,报错同上。

第二次

@Mapper + 启动类排除自动装载: @SpringBootApplication(exclude = { MybatisAutoConfiguration.class }) + 启动类增加自动装载@ImportAutoConfiguration(MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class)

@ImportAutoConfiguration 改为 @Import 就没用了!失败在:AutoConfiguredMapperScannerRegistrar的registerBeanDefinitions时 AutoConfigurationPackages.get()获取不到auto-configuration package 的路径。

结果:正常启动, 且只执行了setSqlSessionFactory方法!

跟踪执行断点发现:

在AbstractAutowireCapableBeanFactory.populateBean()中处理最后得到的PropertyValues中只有SqlSessionFactory

9805f34d509a152b6c22c484e20e6d41.png

后续测试若增加显示声明 @Bean SqlSessionTemplate,又出现了!

关键在于AbstractAutowireCapableBeanFactory.populateBean方法中执行autowireByType时对于resolveDependency结果是否为空的判定!详细分析见:https://my.oschina.net/u/3434392/blog/3010046 的【反向验证】

第三次

启动类增加@MapperScan({ "com.noob.giant.dao" }) + 启动排除自动装载: @SpringBootApplication(exclude = { MybatisAutoConfiguration.class })

结果:正常启动!且创建MapperFactoryBean只执行了setSqlSessionFactory方法! 与第二次的测试呈现一致.

@Mapper & @MapperScan 混用并不会导致重复扫描。优先使用了@MapperScan声明的org.mybatis.spring.annotation.MapperScannerRegistrar,跳过了MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar。原因见下图

57cbafab3b5779c376649d50a7b405d0.png

测试3 - auto-configuration package 一定是启动类包路径

将启动的Application类移动到“com.noob.giant.hh”路径下 :

(因为启动时默认扫描的是启动类包路径下的对象,所以本测试用例需要在启动Application类重新指定 @SpringBootApplication(scanBasePackages={"com.noob.giant"}, 否则加载不到bean)

MybatisAutoConfiguration的静态内部类MapperScannerRegistrarNotFoundConfiguration装配的另一个静态内部类AutoConfiguredMapperScannerRegistrar获取到扫描mapper类的包路径与启动application类包路径一致。

734f91b8840588b6bed007c61704c998.png

测试4 -  单独使用@MapperScan时,指定的扫描包路径一定要精确到Dao层!

一个大范围包路径的话,在ClassPathMapperScanner.doScan时会把一些额外的接口也当作mapper。  如果精确到Dao层就没有这个现象!

44f07582140e123d7396aed394138e04.png

dcaf11cad56047c0701daae9da2bd3b8.png

这是因为:

ClassPathMapperScanner.doScan  ->ClassPathBeanDefinitionScanner.doScan  -> ClassPathScanningCandidateComponentProvider.findCandidateComponens

-> ClassPathScanningCandidateComponentProvider.scanCandidateComponents->ClassPathMapperScanner.isCandidateComponent(AnnotatedBeanDefinition beanDefinition) 方法对扫描得到的AnnotationMetadata判定:

但只是判定bean的定义描述是否是接口。

@Override

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {

return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();

}

所以导致doScan方法最终返回的BeanDefinitionHolder集合中含有很多不相干的接口信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值