MyBatis源码学习五——Mybatis与Springboot整合及Mapper的注入过程

一、环境配置

1.1 pom.xml

 <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

1.2 启动类(Application)加上@MapperScan(“Mapper接口所在包路径”)

在这里插入图片描述

1.3 application.yml

spring:
  application:
    name: account-service
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis_demo?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
    username: root
    password: root

Springboot默认采用的数据源类型是HikariDataSource,全部采用默认配置即可

1.4 BlogMapper.java

在MapperScan扫描路径下新建

@Mapper
public interface BlogMapper {
    List<Blog> selectBlog(String deptId);
}

二、Mapper的注入过程

2.1 @MapperScan

先看看注解@MapperScan,上面有一行@Import(MapperScannerRegistrar.class),说明它依赖MapperScannerRegistrar.class,spring会将它实例化
在这里插入图片描述

2.2 MapperScannerRegistrar.java

在这里插入图片描述
MapperScannerRegistrar.java实现了ImportBeanDefinitionRegistrar接口,因此会回调registerBeanDefinitions方法,往下走会进入重载方法 registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,generateBaseBeanName(importingClassMetadata, 0));
在这里插入图片描述
可以看出这个方法就是要将MapperScannerConfigurer.class注入容器
在这里插入图片描述
在注入之前给basePackage属性设置了值,就是Mapper包路径
在这里插入图片描述
MapperScannerConfigurer进入了registry。

2.3 MapperScannerConfigurer.java

在这里插入图片描述
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor,在注入registry之后就会执行postProcessBeanDefinitionRegistry方法
在这里插入图片描述
进入

scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));–>doScan(basePackages);–>Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

在这里插入图片描述
这块就是扫描指定包路径下的所有class文件,放入beanDefinitions返回,回到ClassPathMapperScanner.doScan,从processBeanDefinitions(beanDefinitions);进入
在这里插入图片描述
红框中给definition设置了属性mapperInterface,并赋值为Mapper.class。然后才将definition(原来是Mapper.class)改成了 MapperFactoryBean.class

2.3.1 Mapper需要加@Repository注解吗

回到postProcessBeanDefinitionRegistry方法看这里
在这里插入图片描述
进入 scanner.registerFilters();
在这里插入图片描述
这里添加了一个includeFilters,意思是所有class都被扫描到,因此Mapper不需要添加@Repository注解

2.4 MapperFactoryBean.java

在这里插入图片描述
MapperFactoryBean继承至SqlSessionDaoSupport,并实现FactoryBean接口,这里用的就是适配器模式。MapperFactoryBean有了FactoryBean包装真正bean的功能(spring容器会对实现了FactoryBean的接口特殊处理,会调用它的getObject方法返回真实bean),同时又有了SqlSessionDaoSupport操作SqlSession 的功能。
MapperFactoryBean里面有些属性,可以看到在processBeanDefinitions(beanDefinitions)都有做设置
在这里插入图片描述
执行完processBeanDefinitions(beanDefinitions)之后,definition的className还是BlogMapper,真实类型是MapperFactoryBean,那么当BlogMapper需要注入时就会调用MapperFactoryBean的getObject。
在这里插入图片描述
最终还是从SqlSession的Configuration里面拿到Mapper的代理实现(由代理工厂MapperProxyFactory产生的MapperProxy),那么Mapper是何时放入的呢?接着看SqlSessionDaoSupport

2.5 SqlSessionDaoSupport.java

在这里插入图片描述
持有SqlSessionTemplate
在这里插入图片描述

SqlSessionTemplate实现SqlSession,它里面有SqlSessionFactory,MapperFactoryBean在实例化之前肯定要先将SqlSessionTemplate实例化,而SqlSessionTemplate实例化又依赖SqlSessionFactory,那么SqlSessionTemplate、SqlSessionFactory在哪里实例化呢?

2.6 MybatisAutoConfiguration.java

2.6.1 sqlSessionFactory

在这里插入图片描述
MybatisAutoConfiguration定义了一个bean(SqlSessionFactory),进入

return factory.getObject();–>afterPropertiesSet();–> this.sqlSessionFactory = buildSqlSessionFactory();

在这里插入图片描述
这块就是给Configuration设置了一些默认值,再返回一个DefaultSqlSessionFactory

2.6.2 sqlSessionTemplate

在这里插入图片描述
这个方法的参数sqlSessionFactory就是上面产生的DefaultSqlSessionFactory,进入

return new SqlSessionTemplate(sqlSessionFactory);–> this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());–> this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));

在这里插入图片描述
得到sqlSessionTemplate4085,持有一个sqlSessionProxy,代理它自己。再用setter方法注入到MapperFactoryBean的sqlSessionTemplate
在这里插入图片描述
看参数也是sqlSessionTemplate4085

2.7 DaoSupport.java

SqlSessionDaoSupport继承至DaoSupport
在这里插入图片描述
DaoSupport实现InitializingBean,在MapperFactoryBean初始化之后执行afterPropertiesSet,进入

checkDaoConfig();–> configuration.addMapper(this.mapperInterface);

在这里插入图片描述
拿到Configuration,将Mapper添加进去

2.8 注入Mapper调用getObject

在这里插入图片描述
此时Configuration里面已经有了Mapper
在这里插入图片描述
取出来赋值给service的属性

三、Mapper方法执行

在这里插入图片描述
执行service的方法调用Mapper接口的方法
在这里插入图片描述
前面讲过的MapperProxy是3.5.4版本,最新版的Mybatis有点小改动。但还是先产生MapperMethod,然后在MapperMethod里面用sqlSession执行,注意这里的SqlSession还是SqlSessionTemplate。进入

mapperMethod.execute(sqlSession, args);–>result = executeForMany(sqlSession, args);–> result = sqlSession.selectList(command.getName(), param);

在这里插入图片描述
sqlSessionProxy代理SqlSessionTemplate自己(为了每个方法都拦截,产生新的SqlSession),继续执行
在这里插入图片描述
每次执行方法前,获取真实的sqlSession,getSqlSession里面会调用sqlSessionFactory的openSession。因此每次执行mapper的方法都会打开新的SqlSession。继续执行就到了Mybatis里面了,前面几篇已经讲过。

四、总结

  1. 扫描mapper包
  2. 生成beanDefinition
  3. 放入BeanDefinitionRegistry
  4. 修改beanDefinition的class为MapperFactoryBean
  5. MapperFactoryBean的getObject,会返回从SqlSession的Configuration里面拿到Mapper的代理实现
  6. 在getObject之前,MapperFactoryBean应该已经存在SqlSession和Configuration
  7. 在MybatisAutoConfiguration中就配置了SqlSessionTemplate(就是SqlSession)、SqlSessionFactory(包含Configuration)
  8. MapperFactoryBean实例化时,将SqlSessionTemplate、SqlSessionFactory注入
  9. 实例化之后注入Service层时调用MapperFactoryBean的getObject,从SqlSession持有的Configuration里拿到mapperRegistry,再取出MapperProxyFactory,调用newInstance生成MapperProxy返回,赋值给Service层的属性
  10. 执行Mapper方法, SqlSessionTemplate的自身代理拦截方法,打开新的SqlSession再执行
  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值