纯mybatis获取mapper对象:

String resource = "mybatis-config.xml";
   InputStream inputStream = Resources.getResourceAsStream(resource);
   SqlSessionFactory sqlSessionFactory = neSqlSessionFactoryBuilder().build(inputStream);
   SqlSession session = sqlSessionFactory.openSession();
   UserMapper mapper session.getMapper(UserMapper.class);
   mapper.selectAll();

Springboot集成mybatis 获取mapper对象:

- 准备工作
  1. application.properties 配置一个数据源
  2. pom引入mybatis的springboot启动器


<dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifctId>
        <version>2.1.3</version>
    </dependency>
  1. 启动类上使用@MapperScan注解扫描mapper接口
使用:直接到需要使用的bean里面即可
@Autowired
    private UserMapper userMapper;
那么问题来了,springboot中,mybatis的mapper接口是如何生成代理对象,并注册到spring ioc容器中的

源码解析

mybatis-spring-boot-starter启动器的作用

引入了以下包

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_工作原理

其中自动装配的包就是mybatis-spring-boot-autoconfigure。

mybatis-spring-boot-autoconfigure包

META-INF下有一个spring.factories文件

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_工作原理_02

导入了这个类MybatisAutoConfiguration

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_实例化_03

根据springboot的spi机制,他会主动去扫描META-INF下的spring.factories中配置的EnableAutoConfiguration的子类,并将其自动装配到spring容器中,这个类的工作就是mybatis集成spring的起源了。

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_工作原理_04

MybatisAutoConfiguration 解析

1.用@Bean的方式SqlSessionFactory,并且将我们自行创建的druid数据源作为参数传了进来

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_spring_05

2.用@Bean的方式创建SqlSessionTemplate,讲将上一步注册到spring容器中的SqlSessionFactory作为参数传递进来。

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_spring_06

那么是如何扫描Mapper接口的呢?

1. @Mapper注解的工作原理 : AutoConfiguredMapperScannerRegistrar
在每个Mapper接口上都打上@Mapper注解,mybatis-spring扫描到就会,如果没有配置@MapperScan,则会默认扫描与Springboot同层级的包下的@Mapper注解的接口。

注册MapperScannerConfigurer的Bean定义到Spring容器中,并设置扫描包的路径

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_实例化_07

MapperScannerConfigurer 实现BeanDefinitionRegistryPostProcessor接口,实例化的时候会调到postProcessBeanDefinitionRegistry方法,这个方法里会创建一个ClassPathMapperScanner对象,然后去扫描

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_实例化_08

扫描到之后修改BeanDefinition

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_实例化_09

1. @MapperScan注解的工作原理,其实和@Mapper扫描之后做的事情一样,只不过扫描的包为@MapperScan注解的basePackage的值,而且配置了@MapperScan,@Mapper将不再被支持。

@MapperScan注解,会import进来MapperScannerRegistrar这个类

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_实例化_10

MapperScannerRegistrar类实现ImportBeanDefinitionRegistrar接口,实例化的时候会调用registerBeanDefinitions方法

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_spring_11

和@Mapper一样,同样会创建MapperScannerConfigurer的BeanDefition,用于后续实例化

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_实例化_12

只不过要扫描的包路径变了,不再是默认的,而是@MapperScan配置的包路径

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_spring_13

后面的话则和@Mapper扫描到之后的工作原理是一样的,扫描到之后,更改BeanDefinition,一毛一样的。

==可以看出@MapperScan最主要的工作原理除了提供BasePackage的值之外,就是用@Import注解导入MapperScannerRegistrar.所以这个注解打在任何可以被spring扫描到的类上都可以,并不一定要打在启动类上(大多数为了只是为了看起来方便,把全局性的配置注解打在启动类上而已)==

2.为什么说配置了@MapperScan,@Mapper将不再被支持。

前面提到,注册扫描@Mapper接口的MapperScannerConfigurer实例的类是AutoConfiguredMapperScannerRegistrar,那么这个类是如何被导入进来的呢

MybatisAutoConfiguration还有一个静态内部类,@Import了AutoConfiguredMapperScannerRegistrar类,但是有@ConditionalOnMissingBean,即spring容器中不存在MapperFactoryBean,MapperScannerConfigurer的实例。

如果@MapperScan注解生效,并且扫描到任意一个Mapper接口(前面被改造成MapperFactoryBean类型的了),那么就不满足注册这个类MapperScannerRegistrarNotFoundConfiguration的实例的条件,继而不会导入AutoConfiguredMapperScannerRegistrar类。

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_spring_14

这里就是熟悉的原生Mybatis创建Mapper接口的味道了。

附上调用的类时序图,回过头来看一下调用的整体流程。

SpringBoot中,mybatis的mapper接口是如何生成代理对象的?_工作原理_15