Mybatis源码1.0

       前言
        Mybatis源码理解记录

        时隔2年左右,再次开启博客记录。今天是开始记录SpringBoot整合Mybatis这个框架的个人理解。


Mybatis初始化
  • spring.factories

    mybatis-spring-boot-autoconfigure.jar中是mybatis初始的所使用的jar。主要通过spring.factories。spring.factories工厂文件通过@SpringBootApplication注解 -> @EnableAutoConfiguration注解 -> @Import(AutoConfigurationImportSelector.class)


    而在AutoConfigurationImportSelector这个类中,getCandidateConfigurations方法 protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations;} 这个方法会加载所有引入的JAR包中下META-INF/spring.factories的工厂文件。
    在mybatis-spring-boot-autoconfigure.jar中所加载的class通过EnableAutoConfiguration加载到Spring容器中

upload successful

  • MybatisAutoConfiguration.class

    • 我们主要关注的Class就是MybatisAutoConfiguration
      upload successful

    • 在这个Class初始化的时候会将MybatisProperties加载进来,而MybatisProperties这里面声明的就是我们在application.yml中所配置的mybatis参数
      upload successful

    • 在MybatisAutoConfiguration中还存在两个静态内部类,

      (MapperScannerRegistrarNotFoundConfiguration、AutoConfiguredMapperScannerRegistrar)在MapperScannerRegistrarNotFoundConfiguration也同样配置了@Import以及@ConditionalOnMissingBean注解
      upload successful

    • AutoConfiguredMapperScannerRegistrar 实现了ImportBeanDefinitionRegistrar;
      所以,spring在refresh的时候,会执行这个类的 registerBeanDefinitions()方法,将 MapperScannerConfigurer存到了beanDefinitionMap中
      upload successful

    • MapperScannerConfigurer是BeanDefinitionRegistryPostProcessor的实现类,在refresh() –> invokeBeanFactoryPostProcessors(beanFactory)中,会遍历所有beanFactoryPostProcessor和BeanDefinitionRegistrtPostProcessor的实现类,依次执行postProcessorBeanDefinitionRegistrar()方法

    • 在将mapper扫描完之后,需要进行sql的解析,在和springboot整合之后,需要在配置文件中配置当前要扫描的mapper.xml文件,mybatis.mapperLocations=classpath:mapper/*Mapper.xml



      这里的mapperLocation,是在sqlSesionFactorybean中进行解析的,在第3步中的自动配置类中,通过@Bean,注入了SqlSessionFactory,
      在sqlSessionFactory()方法最后,会调用factoryBean.getObject()方法,这里其实调用的就是SqlSessionFactory的getObject()方法

      @Override
       public SqlSessionFactory getObject() throws Exception {
            if (this.sqlSessionFactory == null) {
                 afterPropertiesSet();
             }
             return this.sqlSessionFactory;
        }

      从afterPropertiesSet()方法,一直往下追,会追到同类中的buildSqlSessionFactory()方法,在这个方法中,判断如果当前mapperlocation不为null,就进行解析

      if (this.mapperLocations != null) {
            if (this.mapperLocations.length == 0) {
              LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
            } else {
              for (Resource mapperLocation : this.mapperLocations) {
                if (mapperLocation == null) {
                  continue;
                }
                try {
                  XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                      targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
                  xmlMapperBuilder.parse();
                } catch (Exception e) {
                  throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
                } finally {
                  ErrorContext.instance().reset();
                }
                LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
              }
            }
        }

      在xmlMapperBuilder.parse()就是原生mybatis在解析xml文件时,需要调用的方法

    • 在service中注入mapper接口,在初始化service,注入依赖的mapper接口时,还是调用的mapperFactorybean.getObject()方法来获取代理对象


      springboot整合mybatis 和 spring+mybatis整合时,解析xml文件有一个区别:
      spring-mybatis是利用mapperFactorybean的checkDao()方法来解析xml,put数据到mappedStatement和knowmappers springboot是利用SqlSessionFactoryBean的getObject()来解析xml,put数据到mappedStatement和knowMappers