Spring Boot 整合 MyBatis

导入依赖

  1. <dependency>
  2. <groupId>org.mybatis.spring.boot</groupId>
  3. <artifactId>mybatis-spring-boot-starter</artifactId>
  4. </dependency>

其中,依赖了 mybatis-spring-boot-autoconfigure.jar

image.png

在mybatis-spring-boot-autoconfigure.jar 中的spring.factories中

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

自动装配了 MybatisAutoConfiguration 这个类

在这个类中,注入了如下几个组件

  • sqlSessionFactory -> @Bean导入的
  • sqlSessionTemplate -> @Bean导入的
  • MapperScannerRegistrarNotFoundConfiguration -> @Configuration导入的
    • @Import({ AutoConfiguredMapperScannerRegistrar.class }) 这个配置类中,又导入了 AutoConfiguredMapperScannerRegistrar

看看导入的 AutoConfiguredMapperScannerRegistrar 干了什么

  1. public static class AutoConfiguredMapperScannerRegistrar
  2. implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware

他继承了 ImportBeanDefinitionRegistrar,通过registerBeanDefinitions可以向IOC容器中注入一些组件

  1. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  2. logger.debug("Searching for mappers annotated with @Mapper");
  3. ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  4. try {
  5. if (this.resourceLoader != null) {
  6. scanner.setResourceLoader(this.resourceLoader);
  7. }
  8. List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
  9. if (logger.isDebugEnabled()) {
  10. for (String pkg : packages) {
  11. logger.debug("Using auto-configuration base package '{}'", pkg);
  12. }
  13. }
  14. scanner.setAnnotationClass(Mapper.class);
  15. scanner.registerFilters();
  16. scanner.doScan(StringUtils.toStringArray(packages));
  17. } catch (IllegalStateException ex) {
  18. logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
  19. }
  20. }

其中的逻辑为:

  • ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); 创建一个类路径下的Mapper扫描器
  • scanner.setAnnotationClass(Mapper.class); 设置扫描的类型
  • scanner.registerFilters(); 注册过滤规则
  • scanner.doScan(StringUtils.toStringArray(packages)); 进行扫描,将感兴趣的Mapper类型的组件,注册到IOC容器中
    • ClassPathBeanDefinitionScanner#doScan() 会调用到父类的doScan方法
    • findCandidateComponents(basePackage) 找寻候选的组件
    • 在findCandidateComponents 中将会调用isCandidateComponent 来进行判断是否为候选的组件
    • 在本类中,将会调用 ClassPathMapperScanner#isCandidateComponent 来进行判断

其逻辑就是要把Mapper类型的接口那些类都扫描进来

  1. @Override
  2. protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
  3. return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  4. }
  • processBeanDefinitions(beanDefinitions); 然后对这些扫描出来的bean定义进行后置处理

其中的逻辑为

  • 设置bean组件初始化时调用的构造函数类型
  • 设置bean定义的实际类型(在IOC容器进行bean初始化的时候,将会初始化的是这个类型)
  • 给bean组件的属性进行赋值(addToConfig,sqlSessionFactory,sqlSessionTemplate)
  • 设置bean组件的自动装配类型 ```java definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59 definition.setBeanClass(this.mapperFactoryBean.getClass());

definition.getPropertyValues().add(“sqlSessionTemplate”, this.sqlSessionTemplate);

definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

  1. <a name="D2ldc"></a>
  2. ## 至此,Mybatis的自动装配已经完成
  3. <a name="bN4P8"></a>
  4. ### 在我们调用Mapper进行sql查询的时候,实际上,获取的类型是上面进行后置处理时,修改成的MapperFactoryBean类型
  5. <a name="lUSxX"></a>
  6. ### 而MapperFactoryBean又是一个FactoryBean类型,在IOC进行getBean的时候,会调用getObject() 获取实际的类型
  7. 具体流程图下
  8. - getObject
  9. - getSqlSession().getMapper(this.mapperInterface); # SqlSessionTemplate#getMapper 在自动装配过程中注入到IOC容器中,且通过后置处理,将其设置到MapperFactoryBean的属性中
  10. - getConfiguration().getMapper(type, this); # 调用Configuration
  11. - mapperRegistry.getMapper(type, sqlSession); # 调用MapperRegistry类
  12. ```java
  13. # MapperRegistry#getMapper源码
  14. public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  15. final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  16. if (mapperProxyFactory == null) {
  17. throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  18. }
  19. try {
  20. return mapperProxyFactory.newInstance(sqlSession);
  21. } catch (Exception e) {
  22. throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  23. }
  24. }
  • mapperProxyFactory.newInstance(sqlSession); # 最终调用MapperProxyFactory的newInstance创建代理对象
    1. public T newInstance(SqlSession sqlSession) {
    2. final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    3. return newInstance(mapperProxy);
    4. }

    所以,最终获取到的Bean实例,实际上是一个 MapperProxy 类型的

    在执行查询语句的时候,就会嗲用MapperProxy#invoke 进行拦截

    最终,调用sqlSession进行语句的执行

SpringBoot整合 Mybatis的流程到此就完结