Mapper 扫描需要依赖 Maybtis/Spring 这个项目。
Mapper 扫描依赖两种方式:
- 通过 @Mapper 注解 (想通过该注解实现扫描 Mapper ,需要依赖 mybatis/spring-boot-starter 这个项目)
- 通过 @MapperScan 注解
无论是 @Mapper 还是 @MapeprScan 注解,底层都是需要去注册一个 MapperScannerConfigurer 的 Bean , 然后通过该 Bean 来实现 Mapper 的主动注入。
@Mapper 注解
@Mapper 注解主要使用在 Mapper 接口上,如果要实现只对 @Mapper 注解的接口实现扫描,则需要引入 mybatis/spring-boot-starter 这个项目。
mybatis/spring-boot-starter
使用方法请参考:/mybatis/spring-boot-starter/blob/master/mybatis-spring-boot-autoconfigure/src/site/zh/markdown/AutoConfiguredMapperScannerRegistrar
则是自动扫描 @Mapper 注解接口的实现类。
但是该类启用的条件,必须是 Spring 没有找到 MapperScannerConfigurer
和 MapperFactoryBean
的 bean 。
源码分析
AutoConfiguredMapperScannerRegistrar
是 MybatisAutoConfiguration
的静态内部类,故这里展示的是 MybatisAutoConfiguration
的源码。
/**
* MybatisAutoConfiguration 是一个配置类
* 启用该配置类需要项目存在 SqlSessionFactory 和 SqlSessionFactoryBean 这两个类(这两个类都在 mybatis/spring 项目中)
* mybatis/spring-boot-starter 会自动引入 mybatis/spring
*/
@(proxyBeanMethods = false)
@ConditionalOnClass({, })
@ConditionalOnSingleCandidate()
@EnableConfigurationProperties()
@AutoConfigureAfter({, })
public class MybatisAutoConfiguration implements InitializingBean {
// 忽略部分源码,这里只展示要讨论的部分
/**
* 对 @Mapper 注解自动扫描的实现类
* 它继承了 ImportBeanDefinitionRegistrar 接口,实现了 registerBeanDefinitions() 方法
* registerBeanDefinitions() 方法主要是创建并注册 MapperScannerConfigurer 的 BeanDefinition
*/
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 如果没有配置自动扫描的包路径,那么则终止扫描 Mapper
if (!()) {
("Could not determine auto-configuration package, automatic mapper scanning disabled.");
return;
}
("Searching for mappers annotated with @Mapper");
// 获取自动扫描的路径
List<String> packages = ();
if (()) {
(pkg -> ("Using auto-configuration base package '{}'", pkg));
}
// 创建一个 MapperScannerConfigurer 的 BeanDefinitionBuilder
// 并且设置 MapperScannerConfigurer 的字段,如 processPropertyPlaceHolders、annotationClass、basePackage 等字段
// 后续 MapperScannerConfigurer 就会通过这些字段信息去扫描包
BeanDefinitionBuilder builder = ();
("processPropertyPlaceHolders", true);
// annotationClass 表示扫描的包中必须要有指定的注解,这里指定要有 Mapper 这个注解
("annotationClass", );
("basePackage", (packages));
BeanWrapper beanWrapper = new BeanWrapperImpl();
Set<String> propertyNames = (()).map(PropertyDescriptor::getName)
.collect(());
if (("lazyInitialization")) {
// Need to mybatis-spring 2.0.2+
("lazyInitialization", "${-initialization:false}");
}
if (("defaultScope")) {
// Need to mybatis-spring 2.0.6+
("defaultScope", "${-default-scope:}");
}
// 默认设置 sqlSessionTemplateBeanName 的值为 sqlSessionTemplate
boolean injectSqlSession = ("-sql-session-on-mapper-scan", ,
);
if (injectSqlSession && instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) ;
Optional<String> sqlSessionTemplateBeanName = Optional
.ofNullable(getBeanNameForType(, listableBeanFactory));
Optional<String> sqlSessionFactoryBeanName = Optional
.ofNullable(getBeanNameForType(, listableBeanFactory));
if (() || !()) {
("sqlSessionTemplateBeanName",
("sqlSessionTemplate"));
} else {
("sqlSessionFactoryBeanName", ());
}
}
(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册 MapperScannerConfigurer 的 BeanDefinition
((), ());
}
// 忽略后面的代码
}
/**
* 该类使用了 @Configuration 注解,故在调用 AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions() 前,先经过这个类的逻辑:
* 如果是已经存在了 MapperFactoryBean 或者是 MapperScannerConfigurer 的 Bean
* 那么就不会加载 AutoConfiguredMapperScannerRegistrar 的 Bean 了,因此也不会去创建 MapperScannerConfigurer 的 BeanDefinition,做后续的 Mapper 扫描了。
* 其中 MapperScannerConfigurer 的 Bean 可以通过使用 @MapperScan 注解来创建(故如果使用了 @MapperScan 注解,就不会自动扫描 @Mapper 注解的 Mapper)
* 或者我们自定义了一个 Bean ,返回的是 MapperFactoryBean ,那么也不会自动扫描 @Mapper 注解的 Mapper
*/
@(proxyBeanMethods = false)
@Import()
@ConditionalOnMissingBean({, })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
@Override
public void afterPropertiesSet() {
(
"Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
}
}
}
@MapperScan 注解
@MapperScan 注解是 mybatis 自动扫描的常用方式。
它通过使用了 @Import
注解,导入了 MapperScannerRegistrar
类,并且后续通过 MapperScannerRegistrar
来做 Mapper 扫描的逻辑。MapperScan
的源码如下:
@Retention()
@Target()
@Documented
@Import()
@Repeatable()
public @interface MapperScan {
// 忽略 MapperScan 注解的成员信息
}
MapperScannerRegistrar
MapperScannerRegistrar
与扫描 @Mapper
注解的 AutoConfiguredMapperScannerRegistrar
一样,也是继承了 ImportBeanDefinitionRegistrar
接口,重写了 registerBeanDefinitions
方法。
另外 MapperScannerRegistrar
内部维护了 RepeatingRegistrar
静态类,该类继承了 MapperScannerRegistrar
,功能与 MapperScannerRegistrar
一致,但是给 @MapperScans
注解使用。
故这里只探讨 MapperScannerRegistrar
的源码。
源码分析
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
/**
* 实现了 ImportBeanDefinitionRegistrar 的 registerBeanDefinitions() 方法
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 通过 () 方法,获取 @MapperScan 注解的成员变量的值
// 如果没有使用到 @MapperScan 注解,那么 () 方法就会返回 null
// 说白了就是判断有没有用到 @MapperScan 注解,如果没有用到,就不会自动扫描 Mapper
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap((()));
if (mapperScanAttrs != null) {
// 调用下面的方法,去创建 MapperScannerConfigurer 的 BeanDefinition
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
/**
* 创建并注册 MapperScannerConfigurer 的 BeanDefinition
* 在创建过程中把 @MapperScan 注解的成员变量
* 都添加到 MapperScannerConfigurer 的 BeanDefinition 的 PropertyValues 上
*/
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
// 先创建 MapperScannerConfigurer 的 BeanDefinition
BeanDefinitionBuilder builder = ();
// 把 MapperScan 注解的成员变量,都添加到 BeanDefinition 的 PropertyValues 上
("processPropertyPlaceHolders", true);
Class<? extends Annotation> annotationClass = ("annotationClass");
if (!(annotationClass)) {
("annotationClass", annotationClass);
}
Class<?> markerInterface = ("markerInterface");
if (!(markerInterface)) {
("markerInterface", markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = ("nameGenerator");
if (!(generatorClass)) {
("nameGenerator", (generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = ("factoryBean");
if (!(mapperFactoryBeanClass)) {
("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
String sqlSessionTemplateRef = ("sqlSessionTemplateRef");
if ((sqlSessionTemplateRef)) {
("sqlSessionTemplateBeanName", ("sqlSessionTemplateRef"));
}
String sqlSessionFactoryRef = ("sqlSessionFactoryRef");
if ((sqlSessionFactoryRef)) {
("sqlSessionFactoryBeanName", ("sqlSessionFactoryRef"));
}
List<String> basePackages = new ArrayList<>();
((("basePackages")).filter(StringUtils::hasText)
.collect(()));
((("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(()));
if (()) {
(getDefaultBasePackage(annoMeta));
}
String lazyInitialization = ("lazyInitialization");
if ((lazyInitialization)) {
("lazyInitialization", lazyInitialization);
}
String defaultScope = ("defaultScope");
if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
("defaultScope", defaultScope);
}
("basePackage", (basePackages));
// for spring-native
(BeanDefinition.ROLE_INFRASTRUCTURE);
// 最后注册 BeanDefinition 到 BeanDefinitionRegistry,给后续 Bean 的创建使用
(beanName, ());
}
private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
return () + "#" + () + "#" + index;
}
private static String getDefaultBasePackage(AnnotationMetadata importingClassMetadata) {
return (());
}
}
MapperScannerConfigurer
MapperScannerConfigurer 是 Mapper 自动注入的核心,它实现了以下这些接口:
BeanDefinitionRegistryPostProcessor
InitializingBean
ApplicationContextAware
BeanNameAware
其中,自动注入 Mapper 的逻辑,主要是实现了 BeanDefinitionRegistryPostProcessor#registerBeanDefinitions()
方法。
源码如下:
// MapperScannerConfigurer 的一些成员变量,用于给扫描 Mapper 在生成 BeanDefinition 时使用
private String basePackage;
private boolean addToConfig=true;
private String lazyInitialization;
private SqlSessionFactory sqlSessionFactory;
private SqlSessionTemplate sqlSessionTemplate;
private String sqlSessionFactoryBeanName;
private String sqlSessionTemplateBeanName;
private Class<?extends Annotation> annotationClass;
private Class<?> markerInterface;
private Class<?extends MapperFactoryBean> mapperFactoryBeanClass;
private ApplicationContext applicationContext;
private String beanName;
private boolean processPropertyPlaceHolders;
private BeanNameGenerator nameGenerator;
private String defaultScope;
/**
* 先去创建 ClassPathMapperScanner , 然后调用 ClassPathMapperScanner#scan() 方法去扫描 Mapper
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){
if(){
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner=new ClassPathMapperScanner(registry);
();
();
();
();
();
();
();
();
();
();
if((lazyInitialization)){
((lazyInitialization));
}
if((defaultScope)){
(defaultScope);
}
();
(
(,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
从上面的源码中看,实际上是去创建 ClassPathMapperScanner
的类,然后调用 scan()
方法来创建 Mapper。
ClassPathMapperScanner#scan()
ClassPathMapperScanner
是 ClassPathBeanDefinitionScanner
的子类,而上面提到的 scan()
方法实际是 ClassPathBeanDefinitionScanner
的。
该方法主要是扫描指定包路径下的 Mapper ,然后转换成 BeanDefinition,这些 BeanDefintion 携带的 beanClass 信息是 MapperFactoryBean 。
并在后续的 Spring Bean 创建流程中,把这些 BeanDefinition 转换成对应的 Bean(本文不讨论 Spring Bean 初始化流程)。
源码如下:
/**
* ClassPathBeanDefinitionScanner#scan() 方法
*/
public int scan(String...basePackages){
int beanCountAtScanStart=();
// 因为 ClassPathMapperScanner 重写了 doScan() 方法
// 故这里实际上是调用 ClassPathMapperScanner#doScan() 方法
doScan(basePackages);
// Register annotation config processors, if necessary.
if(){
();
}
return(()-beanCountAtScanStart);
}
/**
* ClassPathMapperScanner#doScan() 方法
*/
public Set<BeanDefinitionHolder> doScan(String...basePackages){
// 调用 ClassPathBeanDefinitionScanner#doScan() 方法 ,扫描 Mapper
Set<BeanDefinitionHolder> beanDefinitions=(basePackages);
if(()){
if(printWarnLogIfNotFoundMappers){
(()->"No MyBatis mapper was found in '"+(basePackages)
+"' package. Please check your configuration.");
}
}else{
// 把扫描得到的 BeanDefinitionHolder 列表元素中的 BeanDefinition 的 beanClass 属性设置成 MapperFactoryBean 类型
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
/**
* ClassPathBeanDefinitionScanner#doScan() 方法
* 这里实际上是扫描指定包路径下的 Mapper , 并将它们封装成 BeanDefinitionHolder 列表返回
*/
protected Set<BeanDefinitionHolder> doScan(String...basePackages){
(basePackages,"At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions=new LinkedHashSet<>();
for(String basePackage:basePackages){
// 从指定包目录下扫描 Mapper
Set<BeanDefinition> candidates=findCandidateComponents(basePackage);
for(BeanDefinition candidate:candidates){
ScopeMetadata scopeMetadata=(candidate);
(());
String beanName=(candidate,);
// 对 BeanDeifintion 设置默认值
if(candidate instanceof AbstractBeanDefinition){
postProcessBeanDefinition((AbstractBeanDefinition)candidate,beanName);
}
if(candidate instanceof AnnotatedBeanDefinition){
((AnnotatedBeanDefinition)candidate);
}
// 判断这个 BeanDefinition 是否存在,正常来说是不存在的,如果存在的话,那就证明这个 Mapper 的名字重名或者其他原因。
// 如果 BeanDefinition 不存在,则添加这个 BeanDefinition 到 BeanDefinitionRegistry 中去
// 并添加到要返回的 BeanDefinition 列表上,给后续转换 MapperFactoryBean 使用
if(checkCandidate(beanName,candidate)){
BeanDefinitionHolder definitionHolder=new BeanDefinitionHolder(candidate,beanName);
definitionHolder=
(scopeMetadata,definitionHolder,);
(definitionHolder);
registerBeanDefinition(definitionHolder,);
}
}
}
return beanDefinitions;
}
/**
* ClassPathMapperScanner#processBeanDefinitions()
* 该方法就是将扫描到的 Mapper 的 BeanDefinitionHolder 的信息进行修改
* 其中包含了将 BeanDefinitionHolder 的 BeanDefinition 的 beanClass 信息修改成 MapperFactoryBean 的逻辑
* 因此后面通过 BeanDefinition 所创建出来的 Bean,实际上是 MapperFactoryBean 类型
*/
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions){
AbstractBeanDefinition definition;
BeanDefinitionRegistry registry=getRegistry();
for(BeanDefinitionHolder holder:beanDefinitions){
definition=(AbstractBeanDefinition)();
boolean scopedProxy=false;
// 如果这个 BeanDefinition 是一个代理类,那么就去拿它代理的 BeanDefinition
if(().equals(())){
definition=(AbstractBeanDefinition)Optional
.ofNullable(((RootBeanDefinition)definition).getDecoratedDefinition())
.map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(()->new IllegalStateException(
"The target bean definition of scoped proxy bean not found. Root bean definition["+holder+"]"));
scopedProxy=true;
}
String beanClassName=();
(()->"Creating MapperFactoryBean with name '"+()+"' and '"+beanClassName
+"' mapperInterface");
// 将 Mapper 全限定类名添加到构造方法的参数进去
().addGenericArgumentValue(beanClassName); // issue #59
try{
// 尝试将 Mapper 全限定类名添加到 BeanDefinition 的 PropertyValues 的 map 中去。
().add("mapperInterface",(beanClassName));
}catch(ClassNotFoundException ignore){
// ignore
}
// 修改 BeanClass 的信息,把 Mapper 的 class 替换成 MapperFactoryBean
();
// 下面的代码,都是将 ClassPathMapperScanner 的成员变量的信息,都添加到 BeanDifinition 的 PropertyValues 中去。
().add("addToConfig",);
// Attribute for MockitoPostProcessor
// /mybatis/spring-boot-starter/issues/475
(FACTORY_BEAN_OBJECT_TYPE,beanClassName);
boolean explicitFactoryUsed=false;
if(()){
().add("sqlSessionFactory",
new RuntimeBeanReference());
explicitFactoryUsed=true;
}else if(!=null){
().add("sqlSessionFactory",);
explicitFactoryUsed=true;
}
if(()){
if(explicitFactoryUsed){
(
()->"Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
().add("sqlSessionTemplate",
new RuntimeBeanReference());
explicitFactoryUsed=true;
}else if(!=null){
if(explicitFactoryUsed){
(
()->"Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
().add("sqlSessionTemplate",);
explicitFactoryUsed=true;
}
if(!explicitFactoryUsed){
(()->"Enabling autowire by type for MapperFactoryBean with name '"+()+"'.");
(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
(lazyInitialization);
if(scopedProxy){
continue;
}
if(ConfigurableBeanFactory.SCOPE_SINGLETON.equals(())&&defaultScope!=null){
(defaultScope);
}
// 对非单例类型的 Mapper 做的逻辑
if(!()){
BeanDefinitionHolder proxyHolder=(holder,registry,true);
if((())){
(());
}
((),());
}
}
}
MapperFactoryBean
MapperFactoryBean 是 Mybatis/Spring 用来表示 Mapper 的 Bean ,它继承了 SqlSessionDaoSupport
,实现了 FactoryBean
接口。
所以在获取 Mapper 对应的 Bean 时,实际上是调用了 FactoryBean#getObject()
方法(关于 FactoryBean
的使用,具体可以去搜索引擎查询,这里就不过不叙述了),返回 MapperProxy
的代理类。
以下为 MapperFactoryBean
的部分源码:
/**
* 只展示要讨论的部分,隐藏掉部分不重要的代码
*/
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
/**
* 该 MapperFactoryBean 所对应的 Mapper class 信息
*/
private Class<T> mapperInterface;
/**
* 实现了 FactoryBean#getObject() 方法
* 内部逻辑其实就是调用了 SqlSessionTemplate#getMapper() 方法
* 返回 Mapper 的代理对象,也就是 MapperProxy 代理类
*/
@Override
public T getObject() throws Exception {
// MapperFactoryBean 继承的 SqlSessionDaoSupport 抽象类,内部维护了 SqlSessionTemplate 这个对象
// getSqlSession() 方法,实际就是返回 SqlSessionTemplate 这个对象
return getSqlSession().getMapper();
}
}
SqlSessionTemplate
SqlSessionTemplate
是线程安全的,以保证与当前事务挂钩。
它基于 Spring 事务来完成事务的提交和回滚操作,它不支持调用 SqlSessionTemplate
的 commit()
、rollback()
、close()
等与事务有关的操作。
以下为 SqlSessionTemplate
的部分源码:
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
/**
* SqlSessionFactory 内部维护了 Configuration 对象
* 而 Configuration 内部维护了 MapperRegistry 对象
* Configuration#getMapper() 方法则是从 MapperRegistry 里面获取 Mapper 的代理对象,也就是 MapperProxy
*/
@Override
public <T> T getMapper(Class<T> type) {
// 从 Configuration 里面获取 Mapper 的代理对象
return getConfiguration().getMapper(type, this);
}
@Override
public Configuration getConfiguration() {
return ();
}
}
Configratuion 和 MapperRegistry
类主要是维护了 MyBatis 的配置信息,其中包含了 Mapper 的信息。
其中它内部维护的 MapperRegistry
存放了 Mapper 的信息。
以下为 MapperRegistry
的部分源码:
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public MapperRegistry(Configuration config) {
= config;
}
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) (type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 通过动态代理去创建一个代理类出来,对应类型为 MapperProxy 这个类
return (sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
从上面的代码中,我们可以看到,每次通过 MapperRegistry 获取 Mapper 的时候,会创建一个新的 MapperProxy 代理对象。
如果我们只通过 MapperRegistry 来获取 Mapper 的代理对象,那么每获取一次,相当于要创建一个新的 MapperProxy 。
而 MyBatis/Spring 通过 MapperFactoryBean ,保证每次去获取 Mapper 的 Bean 对象前,会去 Bean 的缓存中获取 Mapper 。
如果 Bean 的缓存不存在该 Mapper ,才去调用 Mapper 对应的 MapperFactoryBean#getObject() 方法,去创建和获取一个新的 MapperProxy 代理对象。