@MapperScan配置在@Configuration注解的类上会导入MapperScannerRegistrar类。

而MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,可以向BeanDefinitionRegistry注册BeanDefinition。

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {

  private ResourceLoader resourceLoader;

  @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); //略。。。 List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
} }

从上述源码可以看到MapperScannerRegistrar的registerBeanDefinitions,从mapperScan注解获取到要扫描的包,将扫描路径提交给ClassPathMapperScanner的doScan方法。

ClassPathMapperScanner继承于ClassPathBeanDefinitionScanner,并覆盖doScan方法,通过父类获取到basePackages路径下的类的beanDefinition,然后修改beanDefinition的beanClass为MapperFactoryBean。

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {

  @Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) {
logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
processBeanDefinitions(beanDefinitions);
} return beanDefinitions;
} private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
} //修改构造方法参数为mapper的class
definition.getConstructorArgumentValues()
.addGenericArgumentValue(definition.getBeanClassName());
//修改beanClass为mapperFactoryBean
definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); //注入sqlSessionFactory/sqlSessionTemplate
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
} if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
} if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
} @Override
//覆盖父类的isCandidateComponent,判断bean为接口且独立
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
} }

最后通过BeanFactory实例化MapperFactoryBean时,因为MapperFactoryBean实现了FactoryBean,会调用getObject生成bean,注册到beanFactory。

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public MapperFactoryBean() {
//intentionally empty
} public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
} @Override
//将mapper注册到Configuration中
protected void checkDaoConfig() {
super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
} @Override
//通过SqlSession获取Mapper代理
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
} }

Mybatis——@MapperScan原理的更多相关文章

  1. SpringBoot集成MyBatis底层原理及简易实现

    MyBatis是可以说是目前最主流的Spring持久层框架了,本文主要探讨SpringBoot集成MyBatis的底层原理.完整代码可移步Github. 如何使用MyBatis 一般情况下,我们在Sp ...

  2. MyBatis工作原理

    Mybatis工作原理: 我们的应用程序通过mybatis提供的api,增删改查方法来访问数据库,api底层调用了jdbc ,只不过mybatis对jdbc的封装是不完全封装,里面的sql语句需要我们 ...

  3. Mybatis插件原理分析(二)

    在上一篇中Mybatis插件原理分析(一)中我们主要介绍了一下Mybatis插件相关的几个类的源码,并对源码进行了一些解释,接下来我们通过一个简单的插件实现来对Mybatis插件的运行流程进行分析. ...

  4. Mybatis的原理与JVM内存结构(面试题)

    Mybatis的原理 1.Mapper 接口在初始SQL SessionFactory注册的 2.Mapper 接口注册在名为MapperRegistry类的 HasMap中 key=Mapper c ...

  5. 面试官:你分析过mybatis工作原理吗?

    Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去.本文建立在Spring+SpringMVC+Mybatis整合的项目之上. 我将其工作原理分为六个部分: 读取核心配置文件 ...

  6. mybatis运行原理

    mybatis运行原理 运行过程中涉及到的类或者接口 Resources(c) :用于加载mybatis核心配置文件 XMLConfigBuilder(c) :用于解析xml文件(核心配置文件) Co ...

  7. mybatis 插件原理

    [传送门]:mybatis 插件原理

  8. 【转】MaBatis学习---源码分析MyBatis缓存原理

    [原文]https://www.toutiao.com/i6594029178964673027/ 源码分析MyBatis缓存原理 1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 ...

  9. 深入理解MyBatis的原理:整个体系

    前言:工作中虽然用到了 MyBatis,可完全不知道为什么,再不学习就晚了,这里将记录我的学习笔记,整个 MyBatis 的体系. 一.简介 1.传统的JDBC JDBC 是一种典型的桥接模式. 使用 ...

随机推荐

  1. Perl如何安装新模块/包

    今天写Perl程序时需要调用到Tk模块,但是我机器上却没有T T. Perl小白,不知道肿么装新模块.网上搜了一下资料,和大家分享下. 本人机器Windows的系统,没法提供Unix或者Linux的测 ...

  2. Flink1.10全文跟读翻译

    前言 突然的一个想法,我想把flink官网英语版全部看一遍翻译出来,并且带上自己的理解.自己不是什么大神,只是想这样做一遍,有人说不是有中文版,因为我自己想练习一下英语和对flink的理解吧!工作是一 ...

  3. DOM-BOM-EVENT(7)

    7.事件深入 7.1.事件捕获 事件流分为事件冒泡和事件捕获两种,事件冒泡指事件从里往外传播,而事件捕获刚好相反,指事件从外向內传播 <!DOCTYPE html> <html la ...

  4. Tomcat的介绍

    Tomcat简介 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选. 支持Se ...

  5. 错误记录——fail: Microsoft.AspNetCore.Server.Kestrel[13]

    fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HLPN4417RVEM", Request id &q ...

  6. sendRedirect()和forward()方法的区别

    sendRedirect()和forward()方法的区别 之前好像曾经整理过,但忘了放在哪里了,好像压根就没整理过,博客里也没有,故今天重新整理一下. 我们知道页面之间的跳转有两种方式:重定向与转发 ...

  7. 洛谷P3237 [HNOI2014]米特运输(树形dp)

    解题报告 题干 米特是D星球上一种非常神秘的物质,蕴含着巨大的能量.在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题. D星上有N个城市,我们将其顺序编号为1到N,1号城市为首都. ...

  8. 洛谷P5774,可爱的动态规划。

    如此可爱的动态规划见过么? 相信各位都非常喜欢动态规划,那我就写一道可爱的动态规划的题解吧. 题目:https://www.luogu.com.cn/problem/P5774 题意: 题意“挺明白” ...

  9. JAVA基础笔记10-11-12-13-14

    十.今日内容介绍 1.继承 2.抽象类 3.综合案例---员工类系列定义 01继承的概述 *A:继承的概念 *a:继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系 *b:在J ...

  10. sql-exists、not exists的用法

    exists : 强调的是是否返回结果集,不要求知道返回什么, 比如:select name from student where sex = 'm' and mark exists(select 1 ...