Spring整合Mybatis

整合的核心思想就是把其他框架所产生的对象放到Spring容器中,让其成为Bean。
单独使用Mybatis框架就需要用到Mybatis所提供的一些类构造出对应的对象,然后使用该对象,就能使用到Mybatis框架给我们提供的功能,和Mybatis整合Spring就是为了将这些对象放入Spring容器中成为Bean,只要成为了Bean,在我们的Spring项目中就能很方便的使用这些对象了,也就能很方便的使用Mybatis框架所提供的功能了。
上代码:直接整springboot整合mybatis,多了自动装配而已,整合mybatis都一样的

上总结:

要处理的问题:mapper是一个接口,接口不能实例化,也就是不能干活,想让他干活的话就要用动态代理,找一个代理类帮他干活。mapper.xml有具体的sql,想让它执行就要把mapper接口的方法和xml中的sql关联起来。
mapper怎么被spring注册为beanDefinition
mapper怎么被实例化
mapper方法怎么被执行

  1. @MapperScan注解通过@Import方法导入MapperScannerRegistrar类,MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,覆写了registerBeanDefinitions方法,作用为手动注册某个Bean的BeanDefinition到容器中【DefaultListableBeanFactory=>beanDefinitionMap】
  2. MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,当执行refresh的invokeBeanFactoryPostProcessors(这个方法的作用是用来执行Bean工厂的后置处理器)会加载所有实现了ImportBeanDefinitionRegistrar接口的类,并执行它的registerBeanDefinitions方法,即执行MapperScannerRegistrar.registerBeanDefinitions
  3. 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition

其中basePackages的值包含@MapperScan注解的"com.junziln.study.order"

  1. MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法
  2. postProcessBeanDefinitionRegistry方法中创建了ClassPathMapperScanner对象,该对象对@MapperScan注解中配置的包路径进行了扫描
  3. 设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中是不会扫描接口的
  4. 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component解决了第一个问题
  5. 为每个mapper接口创建对应的BeanDefinition实例,并修改所有实例中的beanClass属性值为MapperFactoryBean,AutowireMode为byType。(通过属性的类型查找JavaBean依赖的对象并为其注入。比如类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用set方法为其注入。)
  6. 将所有mapper接口的BeanDefinition实例注册到Spring的容器中,为下一步实例化mapper接口做准备。
  7. MapperFactryBean集成了SqlSessionDaoSupport类,实现了FactoryBean接口,覆写了getObject()方法。调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,最后将代理对象注册到了Spring容器中。,但是单例池中放的依旧是MapperFactryBean的bean,代理对象是在代理对象的缓存池中,解决了第二个问题
  8. sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生
  9. MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
  10. 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性
  11. 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象

代码

核心依赖

<dependencies>
  <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.22</version>
  </dependency>
  <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.3.0</version>
  </dependency>
</dependencies>

代码

package com.junziln.study.order;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@MapperScan("com.junziln.study.order.mapper")
public class JunzilnOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(JunzilnOrderApplication.class, args);
    }

}

server:
  port: 19903
spring:
  datasource:
    url: jdbc:mysql://101.43.187.71:3306/blog_demo?characterEncoding=UTF-8&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&tinyInt1isBit=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: 
    password: 
    type: com.alibaba.druid.pool.DruidDataSource
  application:
    name: junziln-order
mybatis:
  mapper-locations: classpath:/mapper/*Mapper.xml


classpath 和 classpaths 区别: classpath:只会到你的class路径中查找找文件; classpaths:不仅包含class路径,还包括jar文件中(class路径)进行查找.**

image.png

分析

创建MapperScannerConfigurer的bean定义及扫描mapper接口

也就是扫描到我们定义的mapper接口,变成BeanDefinition。
这里是两个问题:
1.接口默认不会被spring装载成BeanDefinition。所以mybatis框架要处理。
2.接口不会被实例化,需要对接口类型进行封装。

  1. 通过@MapperScan导入了MapperScannerRegistrar类

在启动类使用了@MapperScan注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
    String[] value() default {};
 
    String[] basePackages() default {};
 
    Class<?>[] basePackageClasses() default {};
}
  1. MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,当执行refresh的invokeBeanFactoryPostProcessors(这个方法的作用是用来执行Bean工厂的后置处理器)会加载所有实现了ImportBeanDefinitionRegistrar接口的类,并执行它的registerBeanDefinitions方法,即执行MapperScannerRegistrar.registerBeanDefinitions
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // 获取@MapperScan注解元数据
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    // @MapperScan注解元数据不为空,注册MapperScannerConfigurer的bean定义
    // generateBaseName(importingClassMetada,0)返回的beanName为
    // com.xx.yy.zz.Application#MapperScannerRegistrar#0
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
          generateBaseBeanName(importingClassMetadata, 0));
    }
  }
 void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
      BeanDefinitionRegistry registry, String beanName) {
    // 实例化MapperScannerConfigurer的bean定义构造者
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    // 通过构造者为bean定义设置属性processPropertyPlaceHolders为true
    builder.addPropertyValue("processPropertyPlaceHolders", true);
    // 获取@MapperScan注解的annotationClass的属性
    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      builder.addPropertyValue("annotationClass", annotationClass);
    }
    // 获取@MapperScan注解的markerInterface的属性
    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      builder.addPropertyValue("markerInterface", markerInterface);
    }
    // 获取@MapperScan注解的nameGenerator的属性
    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
    }
    // 获取@MapperScan注解的factoryBean属性
    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
    }
    // 获取@MapperScan注解的sqlSessionTemplateRef属性
    String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
    if (StringUtils.hasText(sqlSessionTemplateRef)) {
      // bean定义中设置属性名为sqlSessionTemplateBeanName,值为@MapperScan注解的sqlSessionTemplateRef属性值
      builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
    }
    // 获取@MapperScan注解的sqlSessionFactoryRef属性
    String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
    if (StringUtils.hasText(sqlSessionFactoryRef)) {
    // bean定义中设置属性名为sqlSessionFactoryBeanName,值为@MapperScan注解的sqlSessionFactoryRef属性值
      builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
    }
    
    List<String> basePackages = new ArrayList<>();
    basePackages.addAll(
// 获取并合并@MapperScan中value、basePackages、basePackageClasses属性       Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
 
    basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
        .collect(Collectors.toList()));
 
    basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
        .collect(Collectors.toList()));
    // basePackages为空,将设置默认的basePackages,详见getDefaultBasePackage(annoMeta)方法
    // 解析@MapperScan注解的类的包名,作为basePackages
    if (basePackages.isEmpty()) {
      basePackages.add(getDefaultBasePackage(annoMeta));
    }
    // 获取@MapperScan注解的lazyInitialization的属性
    String lazyInitialization = annoAttrs.getString("lazyInitialization");
    if (StringUtils.hasText(lazyInitialization)) {
      builder.addPropertyValue("lazyInitialization", lazyInitialization);
    }
    // 获取@MapperScan注解的defaultScope属性
    String defaultScope = annoAttrs.getString("defaultScope");
    if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
      builder.addPropertyValue("defaultScope", defaultScope);
    }
    // bean定义添加basePackage属性
    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
 
    // for spring-native
    // 设置bean定义的role成员变量,ROLE_INFASTRUCTURE表示该bean为后台角色
    builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // registry中注册bean定义
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
 
  }
  1. 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition

其中basePackages的值包含@MapperScan注解的"com.junziln.study.order"

  1. 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法,在invokeBeanFactoryPostProcessors方法里会执行

image.png

  1. 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描mapper
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
        this.processPropertyPlaceHolders();
    }
	// 构建一个ClassPathMapperScanner,并填充相应属性
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(this.lazyInitialization)) {
        scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
    }
 
    if (StringUtils.hasText(this.defaultScope)) {
        scanner.setDefaultScope(this.defaultScope);
    }
	// 注册Filter,因为上面构造函数我们没有使用默认的Filter,
    // 有两种Filter,includeFilters:要扫描的;excludeFilters:要排除的
    scanner.registerFilters();
    // 扫描basePackage,basePackage可通过",; \t\n"来填写多个,
    // ClassPathMapperScanner重写了doScan方法
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
  1. 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component,解决了第一个问题
public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    // 进行真正的扫描,调用的ClassPathMapperScanner中的doScan
    this.doScan(basePackages);
    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }
	// 返回注册BeanDefinition的个数
    return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 调用父类(ClassPathBeanDefinitionScanner)去扫描指定包下面的类
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
        LOGGER.warn(() -> {
            return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.";
        });
    } else {
        // 将扫描到的beanDefinitions进行处理,把beanDefinition的beanClass替换为
        this.processBeanDefinitions(beanDefinitions);
    }
 
    return beanDefinitions;
}
@Override
  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  }
  1. 上面主要的作用是扫描指定包下的类,并生成bean定义,然后注册到beanFactory的bean定义注册表中,并返回beanDefinitions。
  2. 继续执行processBeanDefinitions(beanDefinitions),接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType
    1. 设置beanClass为MapperFactryBean设置beanClass为MapperFactryBean?

因为mapper是接口,但是接口是不能实例化的,所以mybatis中就把mapper的beanDefinition的beanClass定义为mapperFactoryBeanClass,利用mapperFactoryBeanClass是通过getObject()来进行实例化,即通过jdk代理的方式,生成的代理对象,
简单地说,这里就是先生成MapperScannerConfigurer的bean定义,并放入bean定义注册表中。然后通过MapperScannerConfigurer.postProcessBeanDefinitionRegistry扫描所有mapper类,创建mapper的bean定义,也是放入bean定义注册表中,并将mapper的bean定义的BeanClass属性值设置为mapperFactoryBeanClass,为后面创建bean做准备

  1. 设置autoWireMode=byType 复习下这个:4、依赖注入

在父类SqlSessionDaoSupport中有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。

No:即不启用自动装配。Autowire默认的值。
byName:通过属性的名字的方式查找JavaBean依赖的对象并为其注入。比如说类Computer有个属性printer,指定其autowire属性为byName后,Spring IoC容器会在配置文件中查找id/name属性为printer的bean,然后使用Seter方法为其注入。
byType:通过属性的类型查找JavaBean依赖的对象并为其注入。比如类Computer有个属性printer,类型为Printer,那么,指定其autowire属性为byType后,Spring IoC容器会查找Class属性为Printer的bean,使用set方法为其注入。
constructor:通byType一样,也是通过类型查找依赖对象。与byType的区别在于它不是使用set方法注入,而是使用构造子注入。。
image.png
如果不使用@MapperScan,会怎么处理?
加载MybatisAutoConfiguration类后,因为MybatisAutoConfiguration内部类AutoConfiguredMapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,当执行refresh的invokeBeanFactoryPostProcessors会加载所有实现了ImportBeanDefinitionRegistrar接口的类,并执行它的registerBeanDefinitions方法,即执行AutoConfiguredMapperScannerRegistrar.registerBeanDefinitions

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    if (!AutoConfigurationPackages.has(this.beanFactory)) {
        MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
    } else {
        MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
        // 获取要扫描的包名,即在哪里找mapper
        // 对照示例,就是{"com.micro.provider"}
        List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
        if (MybatisAutoConfiguration.logger.isDebugEnabled()) {
            packages.forEach((pkg) -> {
                MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
            });
        }
		// 使用BeanDefinitionBuilder来构造MapperScannerConfigurer的BeanDefinition对象
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        builder.addPropertyValue("processPropertyPlaceHolders", true);
        // 这就是要扫描的注解类型,就是@Mapper
        builder.addPropertyValue("annotationClass", Mapper.class);
        // 这里是要扫描的包的路径
        builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
        BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
        Set<String> propertyNames = (Set)Stream.of(beanWrapper.getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
        if (propertyNames.contains("lazyInitialization")) {
            builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
        }
 
        if (propertyNames.contains("defaultScope")) {
            builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
        }
		// 注册该MapperScannerConfigurer的bd
        registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
    }
}

这里也是生成MapperScannerConfigurer的bean定义,放到IOC容器(beanFactory)中,后面扫描mapper的逻辑和上面的是一个方法,但是执行后,发现没有找到mapper,报错:“Field userMapper in com.micro.provider.service.impl.UserServiceImpl required a bean of type ‘com.micro.provider.dao.UserMapper’ that could not be found.”,什么原因呢?上面的代码,已经说明了会扫描带@Mapper注解的作为mapper,所以需要在mapper接口上添加@Mapper注解

扫描整体流程

  • .先注册MapperScannerConfigurer,它实现了BeanDefinitionRegistryPostProcessor接口。这个过程是在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法第一次执行,

方法调用顺序为:
AbstractApplicationContext#refresh方法的invokeBeanFactoryPostProcessors(beanFactory)方法 ->
PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法中的invokeBeanDefinitionRegistryPostProcessors方法 -> ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法中的processConfigBeanDefinitions方法 -> parser.parse(candidates)和this.reader.loadBeanDefinitions(configClasses)方法。
这两个关键方法很关键。
parser.parse(candidates)负责处理compnentscan controller等。关键的一点是会把实现了ImportBeanDefinitionRegistrar接口的bean放到ConfigurationClass属性的importBeanDefinitionRegistrars属性中。这其中就包括@mapperscan注解引入的MapperScannerRegistrar。
然后ConfigurationClassBeanDefinitionReader类的loadBeanDefinitions方法则会处理实现了ImportBeanDefinitionRegistrar接口的bean,调用他们的registerBeanDefinitions方法,这是方式之一,
方式之二就是没加@mapperscan注解的话,由mybatis自动配置类实现,他会引入另一个实现了ImportBeanDefinitionRegistrar接口的bean,也就是AutoConfiguredMapperScannerRegistrar,然后引入MapperScannerConfigurer的过程就和方式1相同了。
关键的MapperScannerConfigurer就是这么来的。

  • 然后就是MapperScannerConfigurer干活的时候了,这个过程是在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors方法第三次执行,能执行是因为他实现了BeanDefinitionRegistryPostProcessor接口,就像把他扫描到的ConfigurationClassPostProcessor一样,但是它干的活也就到这了,后面是交给了

ClassPathBeanDefinitionScanner及他的子类ClassPathMapperScanner,方法是ClassPathBeanDefinitionScanner#doScan。ClassPathMapperScanner由mybatis提供,重写isCandidateComponent方法保证mapper接口也可以被spring扫描到。
doScan方法针对加没加@mapperscan(这里说的加没加,指的是容器中有没有MapperScannerConfigurer这个bean,看这个:添加)也有两种逻辑,加了的话会扫描到所有mapper,没加的话在自动配置类(MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar类的registerBeanDefinitions方法中)指明@mapper注解也要被扫描到。

  • 再总结一下,先注册MapperScannerConfigurer(通过自动配置或mapperscan注解),然后通过MapperScannerConfigurer,实现mapper的扫描,具体工作的bean为ClassPathBeanDefinitionScanner及他的子类ClassPathMapperScanner。
  1. 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean
  2. 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean

实例化SqlSessionFactory、SqlSessionTemplate及mapper三个bean

执行到refresh.finishBeanFactoryInitialization(beanFactory),
那么mapper、SqlSessionFactory、SqlSessionTemplate实例化的先后顺序是怎样的呢? 这里需注意一点,在每个创建bean实例之后,初始化bean之前,会执行populateBean进行属性赋值,如果依赖其他的bean,那么会先创建依赖的bean,所以,即使先实例化的是controller(正常controller依赖的service,service依赖mapper,mapper依赖sqlSessionTemplate,sqlSessionTemplate依赖SqlSessionFactory,但实例化的顺序是反过来的),最终还是先实例化SqlSessionFactory。

  1. sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生

实例化SqlSessionFactory

因为SqlSessionFactory在开始的MybatisAutoConfiguration就已经用@Bean进行注解,所以会执行到
(在加载mybatis自动配置类时,会先判断SqlSessionFactory(mybatis jar包中)类和SqlSessionFactoryBean(mybatis-spring jar包中)类是否引入了,这里肯定是引入了(在mybatis-starter中),这里是为了注入bean,只是扫描到了SqlSessionFactory类,但是并没有实例化,所以会在这里进行实例化)
image.png
传入的是datasource,这个应该是在配置文件配置好的,很重要,保存数据库的连接信息,会被封装到SqlSessionFactory唯一的一个属性Configuration属性的Environment属性中
最后会调用SqlSessionFactoryBean的getObject,构建SqlSessionFactory,返回SqlSessionFactoryBean的SqlSessionFactory属性。**** **image.pngimage.png
image.png
buildSqlSessionFactory就是通过XMLConfigBuilder解析mybatis配置,通过XMLMapperBuilder解析mapper.xml的配置(重点是生成mappedStatements、resultMaps、sqlFragments等),以及其他的配置,最终放到Configuration里,供后面使用。
在XMLMapperBuilder.parse()里通过mapperRegistry.addMapper方法,会把mapper接口添加到knownMappers中(knownMappers结构为HashMap,每个mapper的value为MapperProxyFactory对象),那么后面调用getMapper的时候就可以直接获取了。所以XMLMapperBuilder.parse()方法,完成了mapper接口和mapperproxyfactory这个mapper代理工厂的绑定。(获取是为了实例化mapper接口,也就是创建mapper代理对象)
这里进行绑定的前提是在resources/mapper/**下面要有*mapper.xml文件,因为mybaitsPlus可能不会写mapper.xml,所以如果没写的话会在实例化mapper的时候进行绑定。afterPropertiesSet方法(InitializingBean接口)

  • configuration创建

image.png
image.png
yml配置
image.png
自定义配置类

sqlSessionTemplate

image.png

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class }, new SqlSessionInterceptor());
  }
  • 用jdk动态代理生成SqlSession代理对象
  • 第一个参数表示生成代理对象的类加载器,第二个参数表示被代理类对象,第三个参数表示代理实现类(里面主要是执行代理对象时要做的事情)

代理实现是SqlSessionTemplate内部类SqlSessionInterceptor,

实例化mapper

image.png
mapper接口在前面被封装成了MapperFactoryBean,实现了InitializingBean接口,所以应该重写该接口的方法afterPropertiesSet,但是在MapperFactoryBean类中没有重新这个方法,而他的父类SqlSessionDaoSupport也没重写,所以是在SqlSessionDaoSupport的父类DaoSupport类中重写的,所以初始化MapperFactoryBean时会先调用DaoSupport中的afterPropertiesSet方法。执行MapperFactoryBean的checkDaoConfig方法

public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
    this.checkDaoConfig();
 
    try {
        this.initDao();
    } catch (Exception var2) {
        throw new BeanInitializationException("Initialization of DAO failed", var2);
    }
}
protected void checkDaoConfig() {
    super.checkDaoConfig();
    Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");
                                //获取SqlSessionTemplate
    Configuration configuration = this.getSqlSession().getConfiguration();
    // 如果configuration中没有该mapper接口,则加载
    // 注意,上面通过xmlMapperBuilder.parse()已经加载了mapper接口,所以这一步判断不会进入
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
        try {
            // 通过 addMapper() 方法加载到configuration中,内部走的也是mapperRegistry.addMapper方法
            configuration.addMapper(this.mapperInterface);
        } catch (Exception var6) {
            this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);
            throw new IllegalArgumentException(var6);
        } finally {
            ErrorContext.instance().reset();
        }
    }
 
}

mapper是什么时候放到Configuration中的呢,请看上面的checkDaoConfig(也可能是在实例化sqlSessionFactory过程中,通过xmlMapperBuilder加载进来的,两种方式最终都是调用mapperRegistry.addMapper方法加载进来)因为mybaitsplus可能没有写mapper.xml文件,所以在初始化sqlSessionFactory时不会解析xml,需要在这里进行addMapper操作。这个操作是在createBean时完成的,完成后 mapper接口就变成了MapperFactoryBean类型。并执行了MapperFactoryBean的父类的父类(DaoSupport)的afterPropertiesSet方法
上面会针对当前mapper创建MapperFactryBean类型的bean,因为mapper的bean定义的classtype就是这个MapperFactryBean类型

MapperFactoryBean.getObject

初始化之后,因为MapperFactoryBean是FactoryBean,所以会执行MapperFactoryBean.getObject(这里执行的前提条件是有service注入了这个mapper,不然是不会调用这个方法的)
为什么呢 因为跟着spring的步骤,mapper接口在调用getBean方法时会加&前缀,然后getObjectForBeanInstance判断如果加了前缀则直接返回。但是通过service的属性注入调用的getbean方法则不会加&前缀,保证了MapperFactoryBean.getObject的方法调用

  • 复习下知识
  • 在没有注入的情况下,依次获取bean,由于bean定义的class是MapperFactoryBean,所以肯定是factorybean,所以beanName就加了&前缀;图1
  • getObjectForBeanInstance判断如果加了前缀则直接返回,返回的并不是代理对象;图2
  • 在通过service注入mapper后,获取AdminLogService的bean,就会进行依赖注入,找到beanName为adminLogMapper的bean,从而取构建bean;图3,这时候就会进入到getObject的方法了。执行getObjectFromFactoryBean; 就会把代理对象放入 FactoryBean 的缓存中,而原本的单例对象MapperFactoryBean则会放到单例对象池中;

image.png
image.png
image.png

// MapperFactoryBean.java
@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}
getSqlSession()获取的就是上面创建的SqlSessionTemplate对象,这是mapper的SqlSession
,然后继续执行SqlSessionTemplate.getMapper
// SqlSessionDaoSupport.java
public SqlSession getSqlSession() {
  return this.sqlSessionTemplate;
}

// SqlSessionTemplate.java
@Override
public <T> T getMapper(Class<T> type) {
  return getConfiguration().getMapper(type, this);
}

// Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return this.mapperRegistry.getMapper(type, sqlSession);
}

// MapperRegistry.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      // mapperRegistry类中内部有knownMappers属性保存的就是
    //namespace指向的dao也就是当前解析的dao
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {// 通过 MapperProxyFactory.newInstance 创建 MapperProxy
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}


// MapperProxyFactory.java
protected T newInstance(MapperProxy<T> mapperProxy) {
    return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
// MapperProxyFactory.java
public T newInstance(SqlSession sqlSession) {
    MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
    return this.newInstance(mapperProxy);
}

Configuration对象的mapperRegistry变量在Mybatis配置解析时赋值,mapperRegistry内部的knownMappers存储所有mapper接口的map信息,key为mapper接口的Class对象,value为持有对应Class对象的MapperProxyFactory实例,以此创建对应的dao的代理类MapperProxy,在该代理类中封装了mapperInterface (是被代理类的class)和SqlSessionTemplate 两个重要属性。
MapperProxyFactory是代理类工厂,用于生成代理对象MapperProxy的代理类,即为mapper接口的实际代理对象。
通过jdk代理方式创建的mapper代理对象,代理实现类为MapperProxy,以后有人调用mapper方法,就会调用代理对象的invoke方法了。也就是MapperProxy的invoke方法了。
最后把实例化mapper的bean放到bean缓存池中

执行

执行mapper方法,实际就是通过jdk代理机制,执行MapperMethod的execute方法。execute方法里会调用session的方法,这个session是sqlSessionTemplate,创建sqlSessionTemplate时创建了一个代理类,也就是SqlSessionInterceptor,然后通过sqlSessionTemplate的jdk代理机制,执行SqlSessionInterceptor的invoke方法,这个方法会获取到DefaultSqlSession,然后执行method.invoke方法,然后通过反射机制,执行DefaultSqlSession.selectList方法。通过configuration.getMappedStatement获取MappedStatement,这时找到了实际的sql了,然后到了Executor模块。执行SimpleExecutor的doQuery方法,然后执行PreparedStatementHandler的query方法,最终调用jdbc操作执行sql,最后解析结果并返回

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值