Spring Boot系列(三):Spring Boot整合Mybatis源码解析

时间:2022-09-25 22:50:23

一、Mybatis回顾

  1、MyBatis介绍

  Mybatis是一个半ORM框架,它使用简单的 XML 或注解用于配置和原始映射,将接口和Java的POJOs(普通的Java 对象)映射成数据库中的记录。

  2、Mybatis整体架构

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

二、Spring Boot整合Mybatis + Druid

  1、在应用中导入maven依赖如下:

     <!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--database pool-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<!--mysql数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

  2、在应用中加配置

  ① 配置Druid数据源参数:

#配置数据源
spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/demo_db?useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.druid.username=root
spring.datasource.druid.password=123qwe
spring.datasource.druid.driverClassName=com.mysql.jdbc.Driver
spring.datasource.druid.initialSize: 5
spring.datasource.druid.minIdle: 5
spring.datasource.druid.maxActive: 20
spring.datasource.druid.maxWait: 60000
spring.datasource.druid.timeBetweenEvictionRunsMillis: 60000
spring.datasource.druid.minEvictableIdleTimeMillis: 300000
spring.datasource.druid.validationQuery: SELECT 1 FROM DUAL
spring.datasource.druid.testWhileIdle: true
spring.datasource.druid.testOnBorrow: false
spring.datasource.druid.testOnReturn: false
spring.datasource.druid.poolPreparedStatements: true
spring.datasource.druid.filters: stat,wall
spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize: 20
spring.datasource.druid.useGlobalDataSourceStat: true
spring.datasource.druid.connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

  ② 编写Druid数据源属性接收类:

/**
* @desc: 自定义druid的属性
* @author: toby
*/
@ConfigurationProperties(prefix = "spring.datasource.druid")
@Data
public class DruidDataSourceProperties {
private String username;
private String password;
private String url;
private String driverClassName;
private Integer initialSize;
private Integer maxActive;
private Integer minIdle;
private Long maxWait;
......
}

  ③ 编写Druid数据源配置类:

/**
* @desc: 自定义druid配置,如不自定义,配置文件设置的属性不生效自行测试
* @author: toby
*/
@Configuration
@EnableConfigurationProperties(value = DruidDataSourceProperties.class)
@MapperScan(basePackages="com.toby.mapper", value="sqlSessionFactory")
public class DruidDataSourceConfig { @Autowired
private DruidDataSourceProperties druidDataSourceProperties; @Bean
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUsername(druidDataSourceProperties.getUsername());
druidDataSource.setPassword(druidDataSourceProperties.getPassword());
druidDataSource.setUrl(druidDataSourceProperties.getUrl());
druidDataSource.setDriverClassName(druidDataSourceProperties.getDriverClassName());
druidDataSource.setInitialSize(druidDataSourceProperties.getInitialSize());
druidDataSource.setMinIdle(druidDataSourceProperties.getMinIdle());
druidDataSource.setMaxActive(druidDataSourceProperties.getMaxActive());
druidDataSource.setMaxWait(druidDataSourceProperties.getMaxWait());
druidDataSource.setFilters(druidDataSourceProperties.getFilters());
druidDataSourceProperties.setPoolPreparedStatements(druidDataSourceProperties.getPoolPreparedStatements());
return druidDataSource;
}
}

  3、在应用中加@MapperScan注解,他的主要作用就是扫描basePackage包下面的TobyMapper接口,然后getBean的时候通过JDK的动态代理,生成代理对象,所以我们程序中看到的是TobyMapper接口,其实是被动态代理过的。验证方式很简单,DEUBG到TobyMapper的方法里面,就可以发现其就是个动态代理

/**
* @desc: spring boot 启动类
* @author: toby
*/
@SpringBootApplication
@MapperScan(basePackages="com.toby.mapper")
public class MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class, args);
}
}

三、源码解析

  1、Mybatis自动装配

  自动装配的流程图:

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  具体详细见Spring Boot系列(二):Spring Boot自动装配原理解析中的Spring Boot自动装配流程图。Mybatis的自动配置类给我们配置了什么组件,我们接下来看下MybatisAutoConfiguration类

  ① SqlSessionFactory:当容器中没有SqlSessionFactory这个类型的Bean的时候,Spring就加载该组件。

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  ② SqlSessionTemplate:同样当容器中没有SqlSessionTemplate这个类型的Bean的时候,Spring就加载该组件。

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  到此SqlSessionFactory和SqlSessionTemplate组件有了。

  2、@MapperScan注解

  @MapperScan注解:他的作用就是扫描basePackages包下面的TobyMapper接口,然后getBean("tobyMapper")的时候把该接口通过JDK的动态代理,生成代理对象,用于和数据库打交道。

  ① 从@MapperScan入手:

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  ② 导入了MapperScanner注册类MapperScannerRegistrar:

  它是一个ImportBeanDefinitionRegistrar,Spring在通过@Import导入Bean的时候,会调用其registerBeanDefinitions,往Spring容器中注册bean定义信息,以便后面可以通过getBean获取到被注册进行的bean的定义信息所对应的Bean,其往Spring容器中注册的是MapperFactoryBean类型的Bean定义信息,为什么是MapperFactoryBean类型,而不是TobyMapper类型?原因很简单,Spring容器在getBean的时候,会忽略掉接口,接口是不能new的,而Spring容器默认在实例化的时候就是通过调用beanDefinition的beanClass属性所对应的类的无参构造方法。

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  ③ 进去到注册bean定义信息的registerBeanDefinitions方法如下:

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  ④ 进入到ClassPathMapperScanner的doScan,其作用是扫描basePackages所有的包,这里ClassPathMapper Scanner继承了Spring的包扫描ClassPathBeanDefinitionScanner

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  重写(覆盖)了判断是否是候选的Component方法isCandidateComponent,因为Spring默认的isCandidateComponent是会过滤掉接口的,显然不满足,所以重写了该方法

  /**
* Spring默认的,独立的非接口,非抽象类
*/
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
} /**
* ClassPathMapperScanner的可以是独立的接口
*/
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}

  接下来进入到处理TobyMapper的bean的定义信息方法:

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  ⑤ 处理TobyMapper的bean定义信息,主要由3个重要改动:

//第一:修改构造函数为有参的构造函数;
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
//第二:修改bean的class为MapperFactoryBean,该MapperFactoryBean是FactoryBean;
definition.setBeanClass(this.mapperFactoryBean.getClass());
//第三:修改注入类型为按照类型注入;
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

  到此,扫描TobyMapper的时候,往Spring容器中注册的是beanClass为MapperFactoryBean,一个有参数的构造函数,按照类型注入的这么一个Bean定义信息。

  3、通过getBean("tobyMapper")获取TobyMapper的动态代理类

  我们知道,此时的tobyMapper的bean定义信息中的beanClass的属性是MapperFactoryBean.class,而MapperFactoryBean又是一个FactoryBean,FactoryBean的特点就是在getBean的时候会调用其getObject方法;

  ① 我们找到MapperFactoryBean的getObject方法:

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  ② 我们再进入org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper方法,其实已经到了Mybatis的逻辑了。

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  ③ 最后调用到org.apache.ibatis.binding.MapperRegistry#getMapper,其实就是创建JDK的动态代理了。

@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//创建代理实例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

  代理的逻辑在org.apache.ibatis.binding.MapperProxy#invoke方法,到此tobyMapper创建完成,可以操作数据库。

四、扫描Mapper和Mapper动态代理对象生成流程图

Spring Boot系列(三):Spring Boot整合Mybatis源码解析

Spring Boot系列(三):Spring Boot整合Mybatis源码解析的更多相关文章

  1. Spring Cloud系列(四):Eureka源码解析之客户端

    一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-netflix-eureka-client.jar的 ...

  2. Mybatis源码解析3——核心类SqlSessionFactory,看完我悟了

    这是昨晚的武汉,晚上九点钟拍的,疫情又一次来袭,曾经熙熙攘攘的夜市也变得冷冷清清,但比前几周要好很多了.希望大家都能保护好自己,保护好身边的人,生活不可能像你想象的那么好,但也不会像你想象的那么糟. ...

  3. Mybatis源码解析&lpar;一&rpar; —— mybatis与Spring是如何整合的?

    Mybatis源码解析(一) -- mybatis与Spring是如何整合的?   从大学开始接触mybatis到现在差不多快3年了吧,最近寻思着使用3年了,我却还不清楚其内部实现细节,比如: 它是如 ...

  4. springboot整合mybatis源码分析

    springboot整合mybatis源码分析 本文主要讲述mybatis在springboot中是如何被加载执行的,由于涉及的内容会比较多,所以这次只会对调用关系及关键代码点进行讲解,为了避免文章太 ...

  5. Mybatis源码解析&lpar;三&rpar; —— Mapper代理类的生成

    Mybatis源码解析(三) -- Mapper代理类的生成   在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...

  6. mybatis源码-解析配置文件(三)之配置文件Configuration解析

    目录 1. 简介 1.1 系列内容 1.2 适合对象 1.3 本文内容 2. 配置文件 2.1 mysql.properties 2.2 mybatis-config.xml 3. Configura ...

  7. Mybatis源码解析,一步一步从浅入深(三):实例化xml配置解析器(XMLConfigBuilder)

    在上一篇文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码 ,中我们看到 代码:XMLConfigBuilder parser = new XMLConfigBuilder(read ...

  8. Mybatis源码解析&lpar;四&rpar; —— SqlSession是如何实现数据库操作的?

    Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的?   如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...

  9. Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取

    在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao ...

随机推荐

  1. &lbrack;poj3321&rsqb;Apple Tree&lpar;dfs序&plus;树状数组&rpar;

    Apple Tree Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 26762   Accepted: 7947 Descr ...

  2. 七、Action动作类

    七.Action动作类(一般用**Action结尾) 1.编写动作类的三种方式 动作类不实现.也不继承任何的接口和类.即动作类是一个非常普通的JavaBean. public class HelloA ...

  3. delphi 14 内容编辑

    撤销 重做 - 复制 剪切 粘贴 删除 - 全选 不选       ///编辑 ///撤销    WebBrowser1.ExecWB(OLECMDID_REDO ,1); ///重做    WebB ...

  4. &lbrack;Javascript&rsqb; Manage Application State with Immutable&period;js

    Learn how Immutable.js data structures are different from native iterable Javascript data types and ...

  5. SilkTest Q&amp&semi;A 9

    Q81:我应该如何存取excel sheet里面的空字符串? A81:定制代码如下: 解决方案1: [-] if sText!=NULL [ ] Page.tfldName.SetText(sText ...

  6. Tkinter 项目-屏保

    参考教程,以及网上资料,针对小白更好理解 关于tkinter的屏保涉及的知识点和思想 项目分析: 屏保启动方式:手动,自动 敲击键盘或者移动鼠标后,或者其他引发事件,则停止 如果屏保是一幅画的话,则没 ...

  7. 使用Pandas将多个数据表合一

    使用Pandas将多个数据表合一 将多张数据表合为一张表,便于统计分析,进行这一操作的前提为这多张数据表互相之间有关联信息,或者有相同的列. import pandas as pd unames = ...

  8. centos下安装 glances 的问题

    开始想安装htop 然后 yum installhtop 没有 yum searchhtop 也没有  然后上github 发现一个比htop还华丽的东西. Glances 大概这个样子的. 可以一览 ...

  9. 【Centos】【Python3】yum install 报错

    运行yum install 安装时报错 File "/usr/libexec/urlgrabber-ext-down", line 28 except OSError, e: Sy ...

  10. HDU 4091 Zombie’s Treasure Chest 分析 难度&colon;1

    Zombie’s Treasure Chest Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/ ...