@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. JavaWeb网上图书商城完整项目--day02-9.提交注册表单功能之servlet层实现

    1.当用户在界面提交注册提交的时候,我们在UerServlet来实现具体的业务方法 标准demo: 1CommonUtils CommonUtils类就两个方法: lString uuid():生成长 ...

  2. JavaWeb网上图书商城完整项目--day02-24.分类模块的相关类创建

    所谓的分类模块:就是显示所有的分类的功能,显示所有的分类在left.jsp页面中 这就是显示所有的分类: 要实现上面的,我们首先创建一个分类模块,该模块需要实现下面的功能 我们先创建上面的java包 ...

  3. android屏幕适配的全攻略2--支持手机各种屏幕密度dpi

    如何为不同密度的屏幕提供不同的资源和使用密度独立的单位. 1 使用密度无关像素 坚决杜绝在布局文件中使用绝对像素来定位和设置大小.因为不同的屏幕有不同的像素密度,所以使用像素来设置控件大小是有问题的, ...

  4. TCP协议粘包问题详解

    TCP协议粘包问题详解 前言 在本章节中,我们将探讨TCP协议基于流式传输的最大一个问题,即粘包问题.本章主要介绍TCP粘包的原理与其三种解决粘包的方案.并且还会介绍为什么UDP协议不会产生粘包. 基 ...

  5. Spring9——通过用Aware接口使用Spring底层组件、环境切换

    通过用Aware接口使用Spring底层组件 能够供我们使用的组件,都是Aware的子接口. ApplicationContextAware:实现步骤:             (1)实现Applic ...

  6. 4W字的后端面试知识点总结(持续更新)

    点赞再看,养成习惯,微信搜索[三太子敖丙]关注这个互联网苟且偷生的工具人. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的 ...

  7. 关于WebServices的调用

    1.使用soapui测试接口是否能通    具体操作步骤请查看:https://www.cnblogs.com/BINDAI/p/13201513.html Soapui请求结果集 2.获得soapu ...

  8. 基于 React 开发了一个 Markdown 文档站点生成工具

    Create React Doc 是一个使用 React 的 markdown 文档站点生成工具.就像 create-react-app 一样,开发者可以使用 Create React Doc 来开发 ...

  9. [POJ3613] Cow Relays(Floyd+矩阵快速幂)

    解题报告 感觉这道题gyz大佬以前好像讲过一道差不多的?然鹅我这个蒟蒻发现矩阵快速幂已经全被我还给老师了...又恶补了一遍,真是恶臭啊. 题意 给定一个T(2 <= T <= 100)条边 ...

  10. JAVASE经典面试问题(必须熟背),你Get到了吗?

    JAVASE经典面试问题(必须熟背) 1. 编译java程序使用什么命令?运行java使用什么命令? javac *.java java 类名 2. 什么是JDK,什么是JRE,JDK与JRE有什么区 ...