springboot整合mybatis

1.pom

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

引入一个这个注解,就有了mybatis的强大功能,神奇
mybaits使用流程:

  • 引入依赖
  • 配置数据库信息
  • 创建数据库对应的实体类
  • 创建mapper接口,定义查询方法 @Mapper
  • 创建mapper对应的xml,编写sql语句
  • 创建service,调用mapper接口的方法 @Service
  • 创建controller,调用service的方法 @Controller

2.自动配置

引入 mybaits starter会引入这个autoconfigure
springboot整合mybatis_第1张图片
MybatisAutoConfiguration引入,项目启动时会扫描到对应的autoconfigure下的spring.factories文件中的配置。
springboot整合mybatis_第2张图片
引入mybaits-starter会自动引入的jar包:
springboot整合mybatis_第3张图片
mybaits自动配置类
springboot整合mybatis_第4张图片

3.扫描mapper接口

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

1.全局配置方式

在启动类加注解:@MapperScan(“xxx.xxx.xxx.mapper”)
作用就是会扫描到配置的包下面的所有mapper接口,扫描这些接口的目的是为了创建代理对象,因为接口不能实例化,需要用代理方式执行接口中的方法。这里用的是JDK动态代理。

这个注解能生效是因为引入了MapperScannerRegistrar类
mapper啥啥啥输入注册,应该就是这个意思
springboot整合mybatis_第5张图片
MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,当执行refresh的invokeBeanFactoryPostProcessors会加载所有实现了ImportBeanDefinitionRegistrar接口的类,并执行它的registerBeanDefinitions方法,即执行MapperScannerRegistrar.registerBeanDefinitions

而MapperScannerRegistrar.registerBeanDefinitions会先创建MapperScannerConfigurer类到beanfactory中。然后再通过MapperScannerConfigurer再去扫描我们自己定义的mapper接口
springboot整合mybatis_第6张图片

MapperScannerConfigurer实现的BeanDefinitionRegistryPostProcessor接口,因为在invokeBeanFactoryPostProcessors方法里会执行两个接口,按先后执行顺序为:

第一个是BeanDefinitionRegistryPostProcessor;
第二个是BeanFactoryPostProcessor。

而执行BeanDefinitionRegistryPostProcessor,会执行postProcessBeanDefinitionRegistry方法,在这里就是MapperScannerConfigurer.postProcessBeanDefinitionRegistry方法
springboot整合mybatis_第7张图片
这里新建了一个ClassPathMapperScanner类,执行它的scan方法,它的父类是ClassPathBeanDefinitionScanner
springboot整合mybatis_第8张图片
注意上面最后一行 this.basepackage就是配置的扫描路径
会调用ClassPathBeanDefinitionScanner的Scan方法
springboot整合mybatis_第9张图片
先执行ClassPathMapperScanner中的doScan,这里会先调用了父类的doScan,也就是ClassPathBeanDefinitionScanner类的doScan方法(功能就是根据配置的包路径,扫描自定义mapper接口,转换成BeanDefinition)。调用之后执行processBeanDefinitions
springboot整合mybatis_第10张图片
扫描包路径下的所有mapper
springboot整合mybatis_第11张图片
一般情况下,**mapper是接口类。默认情况下,spring对接口interface是不会生成BeanDefinition 对象。在mybatis里,为了生成BeanDefinition 对象,则重写了isCandidateComponent方法,上面箭头指的方法就会调用,是接口类型也要生成BeanDefinition **。
这里解决了标题3的第一个问题。
springboot整合mybatis_第12张图片

通过这个方法就可以生成我们自己的mapper接口的beandefinition了。

spring是扫描所有基于@Component注解的类,并生成bean定义,然后放到bean定义注册表中。而我们这里的mapper没有加@Component注解,所以在执行上述逻辑之前,实际的bean定义注册表中是没有这些mapper的bean定义的。执行上述逻辑之后,才在bean定义注册表中加入了这些mapper的beanDefinition

将beanDefinitions放到beanFactory之后,还有一步重要的操作,就是执行本类ClassPathBeanDefinitionScanner中的processBeanDefinitions
springboot整合mybatis_第13张图片
这里是把每个mapper的Bean定义的BeanClass设置为mapperFactoryBeanClass,这样做是为了让后面创建bean时,可以使用MapperFactoryBean来创建bean。
springboot整合mybatis_第14张图片
springboot整合mybatis_第15张图片
保存了自定义mapper的信息,并将类型变为mapperFactoryBeanClass。这样动态代理的时候就可以找到我们自定义的mapper接口了。
springboot整合mybatis_第16张图片

为什么要把mapper的BeanClass设置为mapperFactoryBeanClass?

因为mapper是接口,但是接口是不能实例化的,所以mybatis中就把mapper的beanDefinition的beanClass定义为mapperFactoryBeanClass,利用mapperFactoryBeanClass是通过getObject()来进行实例化,即通过jdk代理的方式,生成的代理对象。
这里解决了标题3的第二个问题。

总结:
先生成MapperScannerConfigurer的bean定义,并放入bean定义注册表中。然后通过MapperScannerConfigurer.postProcessBeanDefinitionRegistry扫描所有mapper类,创建mapper的bean定义,也是放入bean定义注册表中,并将mapper的bean定义的BeanClass属性值设置为mapperFactoryBeanClass,为后面创建bean做准备。

2.单接口配置方式

@mapper
这样需要在每一个mapper接口添加此注解
这个注解是怎么生效的呢
springboot整合mybatis_第17张图片
加载MybatisAutoConfiguration类后,因为MybatisAutoConfiguration内部类AutoConfiguredMapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口,当执行refresh的invokeBeanFactoryPostProcessors会加载所有实现了ImportBeanDefinitionRegistrar接口的类,并执行它的registerBeanDefinitions方法,即执行AutoConfiguredMapperScannerRegistrar.registerBeanDefinitions。这个概念和全局配置那种方式是一样的。
springboot整合mybatis_第18张图片
到这就已经把mapper接口的定义信息存到beanFactory了,等待实例化。

4.实例化

接下来,执行到refresh.finishBeanFactoryInitialization(beanFactory),在这里将会把bean定义注册表中的所有的beanDefinition进行实例化,然后放到bean缓存池中,供应用程序调用。

注意这里是把bean定义注册表中的所有beanDefinition遍历处理挨个进行实例化,那么mapper、SqlSessionFactory、SqlSessionTemplate实例化的先后顺序是怎样的呢? 这里需注意一点,在每个创建bean实例之后,初始化bean之前,会执行populateBean进行属性赋值,如果依赖其他的bean,那么会先创建依赖的bean,所以,即使先实例化的是controller(正常controller依赖的service,service依赖mapper,mapper依赖sqlSessionTemplate,sqlSessionTemplate依赖SqlSessionFactory,但实例化的顺序是反过来的),最终还是先实例化SqlSessionFactory。

1.SqlSessionFactory

在加载mybatis自动配置类时,会先判断SqlSessionFactory(mybatis jar包中)类和SqlSessionFactoryBean(mybatis-spring jar包中)类是否引入了,这里肯定是引入了(在mybatis-starter中),这里是为了注入bean,只是扫描到了SqlSessionFactory类,但是并没有实例化,所以会在这里进行实例化。
springboot整合mybatis_第19张图片
传入的是datasource,这个应该是在配置文件配置好的
最后会调用getObject
springboot整合mybatis_第20张图片
springboot整合mybatis_第21张图片
springboot整合mybatis_第22张图片
继续调用buildSqlSessionFactory(),主要就是填充前面创建的configuration对象(在MybatisAutoConfiguration类的sqlSessionFactory方法中创建的)。

springboot整合mybatis_第23张图片

springboot整合mybatis_第24张图片
这个configuration里有很多属性,
springboot整合mybatis_第25张图片
springboot整合mybatis_第26张图片
对应的xml
springboot整合mybatis_第27张图片
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接口和xml映射文件的绑定。

  • configuration创建
    springboot整合mybatis_第28张图片
    springboot整合mybatis_第29张图片
    yml配置
    springboot整合mybatis_第30张图片
    自定义配置类
    springboot整合mybatis_第31张图片
  • buildSqlSessionFactory方法
  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      //就是上面创建的configuration对象
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
      LOGGER.debug(
          () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }

    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);

    if (hasLength(this.typeAliasesPackage)) {
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
          .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
    }

    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      });
    }

    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }

    if (hasLength(this.typeHandlersPackage)) {
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
          .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
    }

    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
      });
    }

    targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);

    if (!isEmpty(this.scriptingLanguageDrivers)) {
      Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
        targetConfiguration.getLanguageRegistry().register(languageDriver);
        LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
      });
    }
    Optional.ofNullable(this.defaultScriptingLanguageDriver)
        .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

    if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
      try {
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));
	//重要1 在这儿解析所有的mapper.xml文件
    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
      	//遍历mapper.xml文件进行解析
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
           	//创建xml的构造器
            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 + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }

    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }

重要1:
springboot整合mybatis_第32张图片

  • parse方法
  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //1.解析mapper标签以及内部的标签
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      //2.构建mapper与dao的关系
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

解析mapper标签以及内部的标签

  • configurationElement方法
  private void configurationElement(XNode context) {
    try {
    	//获取namespace 也就是mapper接口
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.isEmpty()) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      //解析parameterMap标签
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //解析resultMap标签
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析sql标签
      sqlElement(context.evalNodes("/mapper/sql"));
      //解析定义的sql语句
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

springboot整合mybatis_第33张图片
list是mapper.xml中的sql语句
springboot整合mybatis_第34张图片
parseStatementNode方法做具体工作,就是解析标签上的所有的属性,然后封装成MappedStatement对象最终放到配置类Configuration的mappedStatements属性上。key 为 id ,mapper接口对应方法的全限定名,value 为 MappedStatement。
执行完parse方法中的 configurationElement(parser.evalNode(“/mapper”))方法:
springboot整合mybatis_第35张图片
这里能获取到两个,早期是通过方法名方式进行查询的。现在是根据全限定名

springboot整合mybatis_第36张图片
构建mapper与dao的映射关系

  • bindMapperForNamespace();
    springboot整合mybatis_第37张图片
    addmapper方法
    springboot整合mybatis_第38张图片

springboot整合mybatis_第39张图片
将mapper接口封装成MapperProxyFactory然后放到knownMappers属性中。
springboot整合mybatis_第40张图片

2.sqlSessionTemplate

在这里插入图片描述
通过动态代理
springboot整合mybatis_第41张图片

  • 上面最后一行代码使用jdk动态代理生成SqlSession代理对象
  • 第一个参数表示生成代理对象的类加载器,第二个参数表示被代理类对象,第三个参数表示代理实现类(里面主要是执行代理对象时要做的事情)

3.mapper

springboot整合mybatis_第42张图片
mapper接口在前面被封装成了MapperFactoryBean,实现了InitializingBean接口,所以应该重写该接口的方法afterPropertiesSet,但是在MapperFactoryBean类中没有重新这个方法,而他的父类SqlSessionDaoSupport也没重写,所以是在SqlSessionDaoSupport的父类DaoSupport类中重写的,所以初始化MapperFactoryBean时会先调用DaoSupport中的afterPropertiesSet方法。
springboot整合mybatis_第43张图片
然后执行MapperFactoryBean的checkDaoConfig方法
springboot整合mybatis_第44张图片
springboot整合mybatis_第45张图片

mapper是什么时候放到Configuration中的呢,请看上面的checkDaoConfig(也可能是在实例化sqlSessionFactory过程中,通过xmlMapperBuilder加载进来的,两种方式最终都是调用mapperRegistry.addMapper方法加载进来)
初始化之后,因为MapperFactoryBean是FactoryBean,所以会执行MapperFactoryBean.getObject,

springboot整合mybatis_第46张图片
上面的getSqlSession()获取的就是上面创建的SqlSessionTemplate对象,这是mapper的SqlSession,然后继续执行SqlSessionTemplate.getMapper,
springboot整合mybatis_第47张图片
springboot整合mybatis_第48张图片
springboot整合mybatis_第49张图片
通过jdk代理方式创建的mapper代理对象,代理实现类为MapperProxy,以后有人调用mapper方法,就会调用代理对象的invoke方法了。也就是MapperProxy的invoke方法了。
最后把实例化mapper的bean放到bean缓存池中,至此,实例化mapper结束。

然后就是前端发请求调用controller,到service,到mapper,到数据库,具体是怎么执行查询了。

5.执行mapper操作

执行mapper是调用的MapperProxy.invoke,最终调用的MapperMethod的execute方法
springboot整合mybatis_第50张图片
执行测试会执行MapperProxy类的invoke方法
springboot整合mybatis_第51张图片
springboot整合mybatis_第52张图片
测试为查询,且没有返回list之类的,所以查询一条
springboot整合mybatis_第53张图片

method和command来自MapperMethod的有参构造函数,去config中拿到sql类型和mapper中方法的全限定名
springboot整合mybatis_第54张图片
这里的sqlSession其实就是上面生成的sqlSessionTemplate,在初始化MapperMethod时候,通过SqlCommand从Configuration中的MappedStatement获取限定名和SQL类型(select|insert|update|delete)
因为sqlSessionTemplate是SqlSessionInterceptor代理创建的,所以,接下来走SqlSessionInterceptor.invoke方法,通过上面实例化sqlSessionTemplate可以看出,实际用的是反射机制,执行sqlSession方法。
springboot整合mybatis_第55张图片
继续执行selectone
springboot整合mybatis_第56张图片
springboot整合mybatis_第57张图片
这里创建的sqlSession是与数据库交互的会话sqlSession,注意与sqlSessionTemplate这个sqlSession的区别,然后通过method.invoke反射调用到具体的 DefaultSqlSession.selectList 方法。
springboot整合mybatis_第58张图片
springboot整合mybatis_第59张图片

最终调用Executor方法执行,Executor有两个方法:BaseExecutor 和 CachingExecutor,分别代表两种缓存机制,BaseExecutor 是一级缓存机制使用,CachingExecutor是二级缓存机制使用(如果查询二级缓存中没有结果,则会调用BaseExecutor的方法查询一级缓存,然后把查询结果放到二级缓存)。
所以先来到二级缓存查询
springboot整合mybatis_第60张图片
springboot整合mybatis_第61张图片
继续执行SimpleExecutor的query方法
springboot整合mybatis_第62张图片
springboot整合mybatis_第63张图片
然后执行doQuery
springboot整合mybatis_第64张图片

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
    	//获取到configuration 
      Configuration configuration = ms.getConfiguration();
      //创建statementHandler
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        //调用 prepareStatement() 方法 获取到 Statement 对象 (真正执行静态SQl的接口)
      stmt = prepareStatement(handler, ms.getStatementLog());
      //调用 StatementHandler.query() 方法执行
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

prepareStatement方法
springboot整合mybatis_第65张图片

springboot整合mybatis_第66张图片
这几个子类的意义:

SimpleStatementHandler ,这个对应的 就是JDBC 中常用到的 Statement 接口,用于简单SQL的处理

PreparedStatementHandler , 这个对应的就是JDBC中的 PreparedStatement,用于预编译SQL的处理

CallableStatementHandler , 这个对应JDBC中 CallableStatement ,用于执行存储过程相关的处理

RoutingStatementHandler,这个接口是以上三个接口的路由,没有实际操作,只是负责上面三个StatementHandler的创建及调用
springboot整合mybatis_第67张图片
springboot整合mybatis_第68张图片
springboot整合mybatis_第69张图片
最后获取结果
springboot整合mybatis_第70张图片
springboot整合mybatis_第71张图片
这里为了测试改了下sql语句
springboot整合mybatis_第72张图片
数据库对应数据
springboot整合mybatis_第73张图片
这样一次完整的查询就走完了。
流程:
执行mapper方法,实际就是通过jdk代理机制,执行MapperMethod的execute方法。然后通过sqlSessionTemplate的jdk代理机制,执行method.invoke方法。然后通过反射机制,执行DefaultSqlSession.selectList方法。通过configuration.getMappedStatement获取MappedStatement,这时找到了实际的sql了,然后到了Executor模块。执行SimpleExecutor的doQuery方法,然后执行PreparedStatementHandler的query方法,最终调用jdbc操作执行sql,最后解析结果并返回。

6.总结

需要处理的问题应该就是:mapper是一个接口,接口不能实例化,也就是不能干活,想让他干活的话就要用动态代理,找一个代理类帮他干活。mapper.xml有具体的sql,想让它执行就要把mapper接口的方法和xml中的sql关联起来
springboot整合mybaits的问题

  • mapper怎么被spring注册为beanDefinition
  • mapper怎么被实例化
  • mapper方法怎么被执行

1.mapper怎么被spring注册为beanDefinition:

mapperscan注解或mapper注解,并且mybatis要重写isCandidateComponent方法,保证是接口也要生成beanDefinition

2.mapper是接口怎么被实例化

注册的时候将mapper接口的类型转换为MapperFactoryBean,然后通过MapperFactoryBean的getObject方法实现实例化(通过jdk代理生成了bean的代理对象)。

3.相关属性初始化

初始化的过程,创建SqlSessionFactory、SqlSessionTemplate、MapperScannerConfigurer的bean定义,放到IOC容器(beanFactory)中,这是基础。在此过程,通过MapperScannerConfigurer扫描指定包下的所有mapper接口生成beanDefinition,并放到bean定义注册表中(类型为:MapperFactoryBean)。

  • SqlSessionFactory
    初始化SqlSessionFactory主要是填充Configuration属性。这里有两个重要参数:MappedStatement和MapperRegistry。
    MappedStatement是解析mapper.xml后封装成的对象。通过XMLMapperBuilder实现。
    在这里也完成了mapper接口与mapper.xml的绑定。MapperRegistry类内的knownMappers缓存: key为namespace对应的dao的class,value为MapperProxyFactory。
  • SqlSessionTemplate
    使用SqlSessionTemplate构造器创建SqlSessionTemplate对象,其中用了jdk代理方式创建了SqlSession代理对象。也就是执行查询的时候调用SqlSessionTemplate的方法实际上调用的是SqlSession的方法。所有访问数据库的操作都是通过SqlSession来的。这么做是为了解耦Mapper和SqlSession。

4.执行mapper

执行mapper方法的过程,主要是先通过两个代理类,即先执行mapper代理实现类MapperProxy的invoke方法,然后执行SqlSessionTemplate代理实现类的invoke方法,然后进入DefaultSqlSession相应方法中,这里会根据mapper的限定名获取MappedStatement,然后调用Executor相应方法,而Executor是封装了jdbc的操作,所以最终是通过jdbc执行sql,最后再把执行的结果解析返回。

整个SpringBoot整合Mybatis的过程,就是在spring容器初始化的过程中生成mapper的代理对象,然后在执行mapper方法的过程,利用代理机制,执行目标方法,最终底层通过jdbc执行sql。

参考链接
https://blog.csdn.net/zhengguofeng0328/article/details/125945926
https://blog.csdn.net/u013521882/article/details/120624374

你可能感兴趣的:(mybatis,spring,boot,java)