mybatis用于和数据库交互层面,我们只需定义好Mapper接口,在业务层需要的地方通过@Autowird引入对应的XxxMapper即可,很方便。为什么@Autowird可以从容器中拿到XxxMapper对象呢?其实是mybatis使用动态代理帮我们生成了XxxMapper的代理对象!下面通过源码来看一下mybatis是如何操作的

1.从@MapperScan说起

@MapperScan属于mybatis的扫描包注解,主要是扫描XxxMapper,下面看@MapperScan的源码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)    //这里导入了MapperScannerRegistrar
public @interface MapperScan {String[] value() default {};......
}

注意@MapperScan注解上面的@Import(MapperScannerRegistrar.class)
在这里导入了MapperScannerRegistrar类,这个类在容器初始化之前会被执行,它主要通过扫描@MapperScan中指定包名,通过层层调用,来生成当前包下XxxMapper的代理对象,并放入容器中。下面来看MapperScannerRegistrar的源码。

MapperScannerRegistrar 类实现了ImportBeanDefinitionRegistrar接口,

MapperScannerRegistrar的源码
在registerBeanDefinitions()方法中,主要是扫描@MapperScan中标注的包中的内容,把MapperFactoryBean注入容器

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {private ResourceLoader resourceLoader;/*** {@inheritDoc}*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//***获取@MapperScan注解标注类的信息AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);// this check is needed in Spring 3.1if (resourceLoader != null) {scanner.setResourceLoader(resourceLoader);}Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");if (!Annotation.class.equals(annotationClass)) {scanner.setAnnotationClass(annotationClass);}Class<?> markerInterface = annoAttrs.getClass("markerInterface");if (!Class.class.equals(markerInterface)) {scanner.setMarkerInterface(markerInterface);}Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");if (!BeanNameGenerator.class.equals(generatorClass)) {scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));}Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));}scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));List<String> basePackages = new ArrayList<String>();//***取到@MapperScan里边的值(即包名)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));}//***spring的框架,暂时不说scanner.registerFilters();//***扫描basePackages(com.luban...)里的内容,源码如下“scanner.doScan(StringUtils.toStringArray(basePackages));}/*** {@inheritDoc}*/@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}}

扫描basePackages(com.luban…)里的内容使用的doScan方法
doScan源码:主要是调用了spring的doScan方法(而非mybatis的),结果不是mybatis想要的,但是拿到了对应类型的BeanDefinitionHolder(bd),再由processBeanDefinitions转成Mybatis想要的

  @Overridepublic Set<BeanDefinitionHolder> doScan(String... basePackages) {//注意:这里的super.doScan调用的是spring的方法,扫描某个包(basePackages)得到对应类型的BeanDefinitionHolder(bd),但是这里的(bd)类型不对不是我们想要的,下面的processBeanDefinitions方法才是mybatis对这个bd做的处理,做成想要的bd。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 {//mybatis处理上边spring扫描的BeanDefinitionHolder(bd),成为mybatis想要的!processBeanDefinitions(beanDefinitions);}return beanDefinitions;}

怎么处理的呢 ,mybatis的processBeanDefinitions源码如下
主要作用:遍历上边spring扫描的每一个XxxMapper,给每一个XxxMapper生成对应的MapperFactoryBean

  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {GenericBeanDefinition definition;//*****循环传进来的spring的doscan方法生成的bdfor (BeanDefinitionHolder holder : beanDefinitions) {definition = (GenericBeanDefinition) holder.getBeanDefinition();if (logger.isDebugEnabled()) {logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");}// the mapper interface is the original class of the bean   // mapper接口是Bean的原始类// but, the actual class of the bean is MapperFactoryBean //,但是,该Bean的实际类是MapperFactoryBean//****添加构造方法,设置接口名字为bean的类型definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59//****在bd中设置mybatis中的FactoryBean == > mapperFactoryBean ,接下来看mapperFactoryBean 的源码definition.setBeanClass(this.mapperFactoryBean.getClass());definition.getPropertyValues().add("addToConfig", this.addToConfig);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);}}}

mybatis中的FactoryBean == > MapperFactoryBean 的源码
主要作用:为上边生成的每一个MapperFactoryBean ,通过 getObject() 方法获取每一个Mapper的代理对象,放入容器

//****implements FactoryBean实现了FactoryBean,注入容器中的对象是 getObject() 中返回的对象
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {//****未指定类型的XxxMapperprivate Class<T> mapperInterface;......//*****通过构造方法注入XxxMapper,给mapperInterface赋值public MapperFactoryBean(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}......//*****调用Mybatis的getSqlSession().getMapper(this.mapperInterface)方法,生成当前mapper的代理对象@Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface);}

2.Mybatis动态代理总结:

1.mybatis定义一个注解@MapperScan
2.在@MapperScan中通过@Import导入了MapperScannerRegistrar,使这个类在启动时被加载。
3.在MapperScannerRegistrar类中,先通过spring扫描得到多个BeanDefinition(bd),后由mybatis的processBeanDefinitions()方法处理上边的多个bd
4.遍历每一个处理后的bd,并添加构造方法,设置接口名字为bean的类型,至此bean类型确定。
5.为每一个处理后的bd,设置FactoryBean(其实就是MapperFactoryBean),
6.在MapperFactoryBean中的getObject()方法中,通过动态代理获取每一个XxxMapper的代理对象,因为上文@Import导入了MapperScannerRegistrar,所以这些代理对象在启动时会被加入到容器,后续在业务层我们就可以随时调用了!

至此,MyBatis通过MapperFactoryBean为每一个XxxMapper生成代理对象结束!

mybatis源码分析、底层原理相关推荐

  1. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  2. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

  3. MyBatis 源码分析 - 内置数据源

    1.简介 本篇文章将向大家介绍 MyBatis 内置数据源的实现逻辑.搞懂这些数据源的实现,可使大家对数据源有更深入的认识.同时在配置这些数据源时,也会更清楚每种属性的意义和用途.因此,如果大家想知其 ...

  4. MyBatis 源码分析 - SQL 的执行过程

    本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析.运 ...

  5. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  6. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  7. MyBatis 源码分析 - 配置文件解析过程

    文章目录 * 本文速览 1.简介 2.配置文件解析过程分析 2.1 配置文件解析入口 2.2 解析 properties 配置 2.3 解析 settings 配置 2.3.1 settings 节点 ...

  8. Mybatis源码分析--关联表查询及延迟加载原理(二)

    在上一篇博客Mybatis源码分析--关联表查询及延迟加载(一)中我们简单介绍了Mybatis的延迟加载的编程,接下来我们通过分析源码来分析一下Mybatis延迟加载的实现原理. 其实简单来说Myba ...

  9. MyBatis 源码分析系列文章合集

    1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...

  10. MyBatis 源码分析-技术分享

    2019独角兽企业重金招聘Python工程师标准>>> MyBatis 源码分析 MyBatis的主要成员 Configuration MyBatis所有的配置信息都保存在Confi ...

最新文章

  1. Prism.Interactivity 之 PopupWindowAction 用法简记
  2. 关于MySQL连接Navicat Premium 12失败的解决方法
  3. Ubuntu14.04系统下安装配置OpenCV 4.0.0开发环境全过程
  4. 第二十二节: 以SQLServer为例介绍数据库自有的锁机制(共享锁、更新锁、排它锁等)和事务隔离级别 :
  5. sqoop从HDFS导出数据到Mysql,卡在Running job: job_1571036741208_0010不动了,或者map 100% reduce 0%不动了
  6. webpack v3 结合 react-router v4 做 dynamic import — 按需加载(懒加载)
  7. 鸿蒙日程管理若离,2020华为HDC日程确定,鸿蒙、HMS以及EMUI 11成最关注点
  8. jQuery 3D圆盘旋转焦点图 支持鼠标滚轮
  9. Akka源码分析-Akka Typed
  10. python 实例方法 类方法_Python实例方法 静态方法 类方法
  11. linux daemon 函数,Daemon 进程的创建
  12. 学校计算机的使用作文,电脑课上作文(3篇)
  13. windows server 2003 asp环境搭建
  14. azure mysql on vnet_管理 VNet 终结点 - Azure CLI - Azure Database for MySQL | Microsoft Docs
  15. [Go]基于Go语言的Web路由转发,多个网站共享一个端口(新版本,支持WebSocket)
  16. Excel使用条件格式
  17. python爬虫代理的使用_从零开始写Python爬虫 --- 2.4 爬虫实践:代理的爬取和验证...
  18. 基于51单片机的温湿度光强二氧化碳气体家庭环境检测proteus仿真
  19. Python爬虫抓取指定网页图片代码实例
  20. matlab回车键,在Matlab中启动.exe文件后,以编程方式按一下回车键

热门文章

  1. Shell命令-系统信息及显示之dmesg、uptime
  2. Intel 14nm PowerVR GPU成功验收:三星、台积电侧目
  3. Guava学习笔记:EventBus(转)
  4. ORACLE获取某个时间段之间的月份列表和日期列表
  5. FastReport的动态页面设置
  6. AdjacentHTML/innerHTML/innerText
  7. 2018-2019-2 网络对抗技术 20162329 Exp5 MSF基础应用
  8. 大数据驱动智能制造 物联网引爆工业革命商机
  9. Android微信跳一跳,自动跳App实现
  10. Java基础-this关键字和构造方法(10)