Mybatis——@MapperScan原理
@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原理的更多相关文章
- SpringBoot集成MyBatis底层原理及简易实现
MyBatis是可以说是目前最主流的Spring持久层框架了,本文主要探讨SpringBoot集成MyBatis的底层原理.完整代码可移步Github. 如何使用MyBatis 一般情况下,我们在Sp ...
- MyBatis工作原理
Mybatis工作原理: 我们的应用程序通过mybatis提供的api,增删改查方法来访问数据库,api底层调用了jdbc ,只不过mybatis对jdbc的封装是不完全封装,里面的sql语句需要我们 ...
- Mybatis插件原理分析(二)
在上一篇中Mybatis插件原理分析(一)中我们主要介绍了一下Mybatis插件相关的几个类的源码,并对源码进行了一些解释,接下来我们通过一个简单的插件实现来对Mybatis插件的运行流程进行分析. ...
- Mybatis的原理与JVM内存结构(面试题)
Mybatis的原理 1.Mapper 接口在初始SQL SessionFactory注册的 2.Mapper 接口注册在名为MapperRegistry类的 HasMap中 key=Mapper c ...
- 面试官:你分析过mybatis工作原理吗?
Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去.本文建立在Spring+SpringMVC+Mybatis整合的项目之上. 我将其工作原理分为六个部分: 读取核心配置文件 ...
- mybatis运行原理
mybatis运行原理 运行过程中涉及到的类或者接口 Resources(c) :用于加载mybatis核心配置文件 XMLConfigBuilder(c) :用于解析xml文件(核心配置文件) Co ...
- mybatis 插件原理
[传送门]:mybatis 插件原理
- 【转】MaBatis学习---源码分析MyBatis缓存原理
[原文]https://www.toutiao.com/i6594029178964673027/ 源码分析MyBatis缓存原理 1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 ...
- 深入理解MyBatis的原理:整个体系
前言:工作中虽然用到了 MyBatis,可完全不知道为什么,再不学习就晚了,这里将记录我的学习笔记,整个 MyBatis 的体系. 一.简介 1.传统的JDBC JDBC 是一种典型的桥接模式. 使用 ...
随机推荐
- 第三模块 :微服务网关Zuul架构和实践
52.Zuul网关架构剖析~1.mp4 请求过来之后首先会通过前置过滤器,然后到路由过滤器,路由过滤器是真正访问后台远程服务的,经过路由器之后,最后会传递给后置过滤器 在三个过滤器处理的过程中任何一个 ...
- laravel Excel 导入
<?php namespace App\Modules\Live\Http\Controllers; use Illuminate\Http\Request; use Maatwebsite\E ...
- 05[掌握]高可用、集群、持久化、docker 等前置知识点
高可用 24小时对外提供服务 高并发 同一时间段能处理的请求数 1,中心化和去中心化 1.1,中心化 意思是所有的节点都要有一个主节点 缺点:中心挂了,服务就挂了 中心处理数据的能力有限,不能把节点性 ...
- Nginx之upstream的四种配置方式
1.轮询(weight) 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况.默认当weight不指定时,各服务器weight相同,每个请求按时间顺序逐一分配到不同的后端服务 ...
- webpack4.X + react搭建
环境准备工作:windows7.webStorm 2017.1.4.Nodejs 8.7.0.npm 5.4.2 PS:安装的时我们都带上版本,这样即便webpack版本发生变化,也不会出现版本问题. ...
- 题解 P1144 【最短路计数】
这道题用一次SPFA就可以过了.在求最短路的同时,对答案进行统计即可. 实现: \(dis_i\)表示从1到\(i\)的最短路(实在还是不懂的话看程序吧). 当\(dis_i>dis_j+1\) ...
- 基于ASP.NET core的MVC站点开发笔记 0x01
基于ASP.NET core的MVC站点开发笔记 0x01 我的环境 OS type:mac Software:vscode Dotnet core version:2.0/3.1 dotnet sd ...
- 原生开发、H5开发、混合移动开发的优缺点
一.原生开发(Native App开发) 原生开发,是在Android.IOS等移动平台上利用官方提供的开发语言.开发类库.开发工具进行App开发.比如Android是利用Java.Eclipse.A ...
- pycharm连接远程服务器(拉取版本)
pycharm连接远程服务器(拉取版本) 有这篇教程是因为,博主有两个办公地点,想着在办公地点A上传代码到服务器并调试,并在B时候可以拉取这份代码,并进行调试.因此有这篇拉取版本的博客,之前还有上传版 ...
- postman-3-请求
请求头 单击Headers选项卡将显示请求头键-值编辑器.我们可以将任何字符串设置为请求头名称.在输入字段时,自动完成下拉菜单将补充常见HTTP请求头. Content-Type标题的值也可从自动完成 ...