MyBatis Plus插件机制与执行流程原理分析

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MyBatis Plus插件机制与执行流程原理分析

【1】MyBatis Plus插件

MyBatis Plus提供了分页插件PaginationInterceptor、执行分析插件SqlExplainInterceptor、性能分析插件PerformanceInterceptor以及乐观锁插件OptimisticLockerInterceptor。


Mybatis 通过插件 (Interceptor) 可以做到拦截四大对象相关方法的执行 ,根据需求完成相关数据的动态改变。注意,这句话是核心哦。

四大对象


Executor

StatementHandler

ParameterHandler

ResultSetHandler

四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),会经过每个插件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理 。四大插件对各自感兴趣的对象、方法、参数如下

① xml下插件的配置

如下所示:

<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
  <!-- 数据源 -->
  <property name="dataSource" ref="dataSource"></property>
  <property name="configLocation" value="classpath:mybatis-config.xml"></property>
  <!-- 别名处理 -->
  <property name="typeAliasesPackage" value="com.jane.mp.beans"></property>
  <!-- 注入全局MP策略配置 -->
  <property name="globalConfig" ref="globalConfiguration"></property>
  <!-- 插件注册 -->
  <property name="plugins">
    <list>
      <!-- 注册分页插件 -->
      <bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></bean>
      <!-- 注册执行分析插件 -->
      <bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor">
        <property name="stopProceed" value="true"></property>
      </bean>
      <!-- 注册性能分析插件 -->
      <bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor">
        <property name="format" value="true"></property>
        <!-- <property name="maxTime" value="5"></property> -->
      </bean>
      <!-- 注册乐观锁插件 -->
      <bean class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor">
      </bean>
    </list>
  </property>
</bean>


② springboot下注册插件

@Bean注解向容器中注入bean,这里以分页插件为例:

@Bean
public PaginationInterceptor paginationInterceptor() {
    PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
    // paginationInterceptor.setOverflow(false);
    // 设置最大单页限制数量,默认 500 条,-1 不受限制
    // paginationInterceptor.setLimit(500);
    // 开启 count 的 join 优化,只针对部分 left join
    paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
    return paginationInterceptor;
}

③ SqlExplainInterceptor

SQL执行分析拦截器,全类名是com.baomidou.mybatisplus.plugins.SqlExplainInterceptor,只支持 MySQL5.6.3以上版本。


该插件的作用是分析 DELETE UPDATE语句 ,防止小白或者恶意进行DELETE UPDATE全表操作,不建议在生产环境中使用会造成性能下降,


在插件的底层通过SQL语句分析命令 Explain 分析当前的 SQL语句,根据结果集中的 Extra列来断定当前是否全表操作。

④ PerformanceInterceptor性能分析插件

性能分析拦截器,全类名是com.baomidou.mybatisplus.plugins.PerformanceInterceptor,用于输出每条 SQL 语句及其执行时间。SQL性能执行分析 ,开发环境使用 超过指定时间,停止运行。

⑤ OptimisticLockerInterceptor乐观锁插件

全类名是com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor。如果想实现如下需求 : 当要更新一条记录的时候,希望这条记录没有被别人更新,就可以使用该插件进行判断。


乐观锁的实现原理(@Version 用于注解实体字段,必须要有) :

  • 取出记录时,获取当前 version
  • 更新时,带上这个version
  • 执行更新时,set version = yourVersion+1 where version = yourVersion
  • 如果 version不对,就更新失败

⑥ InterceptorChain

拦截器链,内部维护了一个私有常量List<Interceptor> interceptors,其pluginAll方法为遍历interceptors并调用每个拦截器的plugin方法。

public class InterceptorChain {
  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }
//添加拦截器到链表中
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  //获取链表中的拦截器
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }
}

在创建SqlSessionFactory会把MybatisSqlSessionFactoryBean.plugins放到InterceptorChain.interceptors 中。


【2】获取sqlSessionFactoryBean

如下图所示,在系统启动时会初始化定义的bean。DefaultListableBeanFactory.preInstantiateSingletons方法中会从beanDefinitionNames中获取bean name然后依次创建。


这里可以看到RootBeanDefinitioncom.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean

① 获取bean的过程中bean属性

如下所示,在getBean过程中可以看到bean的属性:


这里属性为plugins,会在bean的实例化时(populateBean)为属性赋值–也就是说会创建四个插件实例。


② createBean

第一次获取bean的时候会走到AbstractAutowireCapableBeanFactory.createBean进行bean的创建。

 /**
 创建一个bean实例,为bean实例设置属性值,调用post-processors-bean后置处理器
 */
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
//...暂时忽略其他代码
//这里会拿到bean的class类型
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
  try {
// 这里会首先触发BeanPostProcessors ,如果这里能获取到bean则直接返回,不再走doCreateBean
    Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    if (bean != null) {
      return bean;
    }
  }
//...暂时忽略其他代码
//如果上面没有获取到bean,则会走doCreateBean--这也是创建bean的核心过程
  Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//...暂时忽略其他代码
  return beanInstance;
}

如下图所示此时sqlSessionFactoryBean拿到的class为

com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean,不再是Mybatis自身的哦。

在AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation中就会分别执行bean后置处理器的前置和后置方法。

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
  // Make sure bean class is actually resolved at this point.
  if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    Class<?> targetType = determineTargetType(beanName, mbd);
    if (targetType != null) {
        // 后置处理器的before方法
      bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
      if (bean != null) {
      // 后置处理器的after方法
        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
      }
    }
  }
  mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}

执行后置处理器的前置方法如下所示:

拿到bean后置处理器,如果其是InstantiationAwareBeanPostProcessor则调用其postProcessBeforeInstantiation方法。


③ doCreateBean


AbstractAutowireCapableBeanFactory.doCreateBean是创建bean的核心方法,这会为bean属性赋值并会触发bean后置处理器、InitializingBean以及自定init方法等。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
  throws BeanCreationException {
// Instantiate the bean.--实例化bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
  instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//获取bean的包装对象-这里很重要
  instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
  if (!mbd.postProcessed) {
    try {
//调用MergedBeanDefinitionPostProcessors的postProcessMergedBeanDefinition方法
      applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    }
    catch (Throwable ex) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
          "Post-processing of merged bean definition failed", ex);
    }
    mbd.postProcessed = true;
  }
}
//...
//这里暂时忽略其他代码
// Initialize the bean instance.--初始化bean实例
Object exposedObject = bean;
try {
//如下方法很重要
  populateBean(beanName, mbd, instanceWrapper);
  if (exposedObject != null) {
  //这里会调用initializeBean方法
    exposedObject = initializeBean(beanName, exposedObject, mbd);
  }
}
//...
// Register bean as disposable.
try {
  registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
//...
return exposedObject;
}

④ populateBean

顾名思义,为bean实例属性赋值。

AbstractAutowireCapableBeanFactory.populateBean

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
//获取属性值列表
PropertyValues pvs = mbd.getPropertyValues();
//...该种符号表示暂时忽略其他代码
//在为bean属性赋值前,给InstantiationAwareBeanPostProcessors 机会修改bean的状态
//应用场景如支持字段注入
boolean continueWithPropertyPopulation = true;
//循环调用InstantiationAwareBeanPostProcessors 的postProcessAfterInstantiation方法
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof InstantiationAwareBeanPostProcessor) {
      InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
      if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
        continueWithPropertyPopulation = false;
        break;
      }
    }
  }
}
if (!continueWithPropertyPopulation) {
  return;
}
//解析autowire注解字段,进行主动注入
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
    mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
  MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
  // Add property values based on autowire by name if applicable.
  if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
    autowireByName(beanName, mbd, bw, newPvs);
  }
  // Add property values based on autowire by type if applicable.
  if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
    autowireByType(beanName, mbd, bw, newPvs);
  }
  pvs = newPvs;
}
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
//循环调用InstantiationAwareBeanPostProcessors 的postProcessPropertyValues方法
if (hasInstAwareBpps || needsDepCheck) {
  PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
  if (hasInstAwareBpps) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
        if (pvs == null) {
          return;
        }
      }
    }
  }
  if (needsDepCheck) {
    checkDependencies(beanName, mbd, filteredPds, pvs);
  }
}
//在这里为属性赋值,会进行类型转换,这里注意关键词deep copy
//如果是引用类型且bean没有存在,则会进行bean的创建过程
applyPropertyValues(beanName, mbd, bw, pvs);
}

如下图所示在创建sqlSessionFactoryBean过程中会创建其属性globalConfiguration对象:

如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性PaginationInterceptor对象:

如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性SqlExplainInterceptor对象:

如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性PerformanceInterceptor对象:


如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性OptimisticLockerInterceptor对象:


⑤ initializeBean

AbstractAutowireCapableBeanFactory.initializeBean源码如下:

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
  if (System.getSecurityManager() != null) {
    AccessController.doPrivileged(new PrivilegedAction<Object>() {
      @Override
      public Object run() {
        invokeAwareMethods(beanName, bean);
        return null;
      }
    }, getAccessControlContext());
  }
  else {
  //调用意识/通知方法
    invokeAwareMethods(beanName, bean);
  }
  Object wrappedBean = bean;
  if (mbd == null || !mbd.isSynthetic()) {
  //调用bean后置处理器的前置方法
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  }
  //调用初始化方法
  try {
    invokeInitMethods(beanName, wrappedBean, mbd);
  }
  catch (Throwable ex) {
    throw new BeanCreationException(
        (mbd != null ? mbd.getResourceDescription() : null),
        beanName, "Invocation of init method failed", ex);
  }
  if (mbd == null || !mbd.isSynthetic()) {
  //  //调用bean后置处理器的后置方法
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  }
  return wrappedBean;
}

AbstractAutowireCapableBeanFactory.invokeInitMethods方法源码如下:

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
      throws Throwable {
  boolean isInitializingBean = (bean instanceof InitializingBean);
  if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
    if (logger.isDebugEnabled()) {
      logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
    }
    //调用InitializingBean.afterPropertiesSet
    if (System.getSecurityManager() != null) {
      try {
        AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
          @Override
          public Object run() throws Exception {
            ((InitializingBean) bean).afterPropertiesSet();
            return null;
          }
        }, getAccessControlContext());
      }
      catch (PrivilegedActionException pae) {
        throw pae.getException();
      }
    }
    else {
      ((InitializingBean) bean).afterPropertiesSet();
    }
  }
//调用自定义初始化方法
  if (mbd != null) {
    String initMethodName = mbd.getInitMethodName();
    if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
        !mbd.isExternallyManagedInitMethod(initMethodName)) {
      invokeCustomInitMethod(beanName, bean, mbd);
    }
  }
}

如下图所示,MybatisSqlSessionFactoryBean同样实现了InitializingBean接口。那么我们就需要注意其afterPropertiesSet方法了。


⑥ afterPropertiesSet

如下图所示,如果bean实现了InitializingBean接口,那么在初始化过程中一定会调用其afterPropertiesSet方法。



MybatisSqlSessionFactoryBean.afterPropertiesSet如下所示,代码很简短只是创建了sqlSessionFactory。

@Override
public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
 //sqlSessionFactoryBuilder在populateBean的applyPropertyValues过程中已经存在!
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
        "Property 'configuration' and 'configLocation' can not specified with together");
    this.sqlSessionFactory = buildSqlSessionFactory();
}

进入afterPropertiesSet()方法前MybatisSqlSessionFactoryBean如下所示:

我们看一下sqlSessionFactory,这是一段很长的过程:

protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
    Configuration configuration;
    // TODO 加载自定义 MybatisXmlConfigBuilder
    MybatisXMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
        configuration = this.configuration;
        if (configuration.getVariables() == null) {
            configuration.setVariables(this.configurationProperties);
        } else if (this.configurationProperties != null) {
            configuration.getVariables().putAll(this.configurationProperties);
        }
    } else if (this.configLocation != null) {
 //通常如果配置了configLocation会从这里创建MybatisXMLConfigBuilder,
 //其构造方法又创建了MybatisConfiguration
        xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
        configuration = xmlConfigBuilder.getConfiguration();
    } else {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
        }
        // TODO 使用自定义配置
        configuration = new MybatisConfiguration();
        if (this.configurationProperties != null) {
            configuration.setVariables(this.configurationProperties);
        }
    }
    if (this.objectFactory != null) {
        configuration.setObjectFactory(this.objectFactory);
    }
    if (this.objectWrapperFactory != null) {
        configuration.setObjectWrapperFactory(this.objectWrapperFactory);
    }
    if (this.vfs != null) {
        configuration.setVfsImpl(this.vfs);
    }
// 类型别名包配置处理
    if (hasLength(this.typeAliasesPackage)) {
        // TODO 支持自定义通配符
        String[] typeAliasPackageArray;
        if (typeAliasesPackage.contains("*") && !typeAliasesPackage.contains(",")
            && !typeAliasesPackage.contains(";")) {
            typeAliasPackageArray = PackageHelper.convertTypeAliasesPackage(typeAliasesPackage);
        } else {
            typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        }
        if (typeAliasPackageArray == null) {
            throw new MybatisPlusException("not find typeAliasesPackage:" + typeAliasesPackage);
        }
        for (String packageToScan : typeAliasPackageArray) {
            configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
            }
        }
    }
    // TODO 自定义枚举类扫描处理
    if (hasLength(this.typeEnumsPackage)) {
        Set<Class> classes = null;
        if (typeEnumsPackage.contains("*") && !typeEnumsPackage.contains(",")
            && !typeEnumsPackage.contains(";")) {
            classes = PackageHelper.scanTypePackage(typeEnumsPackage);
        } else {
            String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage,
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
            if (typeEnumsPackageArray == null) {
                throw new MybatisPlusException("not find typeEnumsPackage:" + typeEnumsPackage);
            }
            classes = new HashSet<Class>();
            for (String typePackage : typeEnumsPackageArray) {
                classes.addAll(PackageHelper.scanTypePackage(typePackage));
            }
        }
        // 取得类型转换注册器
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        for (Class cls : classes) {
            if (cls.isEnum()) {
                if (IEnum.class.isAssignableFrom(cls)) {
                    typeHandlerRegistry.register(cls.getName(), com.baomidou.mybatisplus.handlers.EnumTypeHandler.class.getCanonicalName());
                } else {
                    // 使用原生 EnumOrdinalTypeHandler
                    typeHandlerRegistry.register(cls.getName(), org.apache.ibatis.type.EnumOrdinalTypeHandler.class.getCanonicalName());
                }
            }
        }
    }
// 类型别名注册
    if (!isEmpty(this.typeAliases)) {
        for (Class<?> typeAlias : this.typeAliases) {
            configuration.getTypeAliasRegistry().registerAlias(typeAlias);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Registered type alias: '" + typeAlias + "'");
            }
        }
    }
//这里会遍历plugins然后调用     configuration.addInterceptor(plugin);
//--这会把plugin放到InterceptorChain.interceptors中
    if (!isEmpty(this.plugins)) {
        for (Interceptor plugin : this.plugins) {
            configuration.addInterceptor(plugin);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Registered plugin: '" + plugin + "'");
            }
        }
    }
    if (hasLength(this.typeHandlersPackage)) {
        String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        for (String packageToScan : typeHandlersPackageArray) {
            configuration.getTypeHandlerRegistry().register(packageToScan);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
            }
        }
    }
// 注册类型处理器,用来处理参数和结果集
    if (!isEmpty(this.typeHandlers)) {
        for (TypeHandler<?> typeHandler : this.typeHandlers) {
            configuration.getTypeHandlerRegistry().register(typeHandler);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Registered type handler: '" + typeHandler + "'");
            }
        }
    }
// databaseIdProvider -据库厂商标识处理
    if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
        try {
            configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
        } catch (SQLException e) {
            throw new NestedIOException("Failed getting a databaseId", e);
        }
    }
// 缓存
    if (this.cache != null) {
        configuration.addCache(this.cache);
    }
    if (xmlConfigBuilder != null) {
        try {
        // xml解析
            xmlConfigBuilder.parse();
            if (LOGGER.isDebugEnabled()) {
                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();
        }
    }
// 事务工厂
    if (this.transactionFactory == null) {
        this.transactionFactory = new SpringManagedTransactionFactory();
    }
    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
    // 设置元数据相关
    GlobalConfigUtils.setMetaData(dataSource, globalConfig);
    // 获取SqlSessionFactory 
    SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration);
    // TODO SqlRunner
    SqlRunner.FACTORY = sqlSessionFactory;
    // TODO 缓存 sqlSessionFactory
    globalConfig.setSqlSessionFactory(sqlSessionFactory);
    // TODO 设置全局参数属性
    globalConfig.signGlobalConfig(sqlSessionFactory);
    if (!isEmpty(this.mapperLocations)) {
        if (globalConfig.isRefresh()) {
            //TODO 设置自动刷新配置 减少配置
            new MybatisMapperRefresh(this.mapperLocations, sqlSessionFactory, 2,
                2, true);
        }
        for (Resource mapperLocation : this.mapperLocations) {
            if (mapperLocation == null) {
                continue;
            }
            try {
                // TODO  这里也换了噢噢噢噢
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                    configuration, mapperLocation.toString(), configuration.getSqlFragments());
                xmlMapperBuilder.parse();
            } catch (Exception e) {
                throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
            } finally {
                ErrorContext.instance().reset();
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
            }
        }
    } else {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
        }
    }
    return sqlSessionFactory;
}


上面代码主要做了如下事情:


获取MybatisXMLConfigBuilder对象

获取Configuration对象-MybatisConfiguration

配置对象Configuration添加插件configuration.addInterceptor(plugin);其会添加到InterceptorChain.interceptors中

xmlConfigBuilder.parse()对configuration做进一步处理

获取SpringManagedTransactionFactory用来创建SpringManagedTransaction

获取一个DefaultSqlSessionFactory实例对象

globalConfig.setSqlSessionFactory(sqlSessionFactory)中会创建MybatisSqlSessionTemplate--SqlSession的实现类

解析mapperLocation对应的一个个mapper配置文件,使用助手builderAssistant的addMappedStatement方法将一个个结点添加配置对象中–这里可以理解为解析SQL完成

其他属性设置等等

也就是说,在MybatisSqlSessionFactoryBean.afterPropertiesSet方法执行结束后,SqlSessionFactory、SqlSessionTemplate、Configuration等都已存在!

【3】查询执行流程分析之实例准备

查询流程可以参考如下图示

① employeeMapper

示例代码如下:

List<Employee > emps = employeeMapper.selectPage(page, null);


如下图所示,此时我们获取到的employeeMapper其实是个代理对象:

1.1如下所示,会执行MapperProxy.invoke方法


1.2 判断SQLtype

MapperMethod.execute中会根据SqlCommand.type进行类型判断:

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
      //这里根据method的返回类型执行不同方法
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

首先根据INSERT、UPDATE、DELETE、SELECT、FLUSH进行判断,然后根据方法返回结果进行判断(如果是select的话)。


1.3 MapperMethod.executeForMany

这里我们走到了executeForMany,该方法解析参数然后调用sqlSession获取结果。

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    //解析参数为map结构
    Object param = method.convertArgsToSqlCommandParam(args);
    //判断是否拥有RowBounds参数---也就是是否需要分页
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
    } else {
    //如果不需要分页,则会查询所有符合条件的结果
      result = sqlSession.<E>selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    //判断返回数组还是集合
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }

sqlSession.<E>selectList(command.getName(), param, rowBounds)

2.1 SqlSessionTemplate.selectList

注意哦,这里是sqlsessionTemplate。

  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
  }


这里调用其代理对象来执行方法,sqlSessionProxy如下所示:

如下图所示,其来到了SqlSessionTemplate$SqlSessionInterceptor.invoke方法中(SqlSessionInterceptor是SqlSessionTemplate的内部类):

2.2 SqlSessionInterceptor

这里会获取SqlSession 实例对象,根据SqlSession 和参数反射调用Method 获取结果。

 private class SqlSessionInterceptor implements InvocationHandler {
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   //这里获取SqlSession 实例对象
     SqlSession sqlSession = getSqlSession(
         SqlSessionTemplate.this.sqlSessionFactory,
         SqlSessionTemplate.this.executorType,
         SqlSessionTemplate.this.exceptionTranslator);
     try {
     //反射调用方法获取结果 交给获取的SqlSession 去处理
       Object result = method.invoke(sqlSession, args);
       if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
         // force commit even on non-dirty sessions because some databases require
         // a commit/rollback before calling close()
         sqlSession.commit(true);
       }
       return result;
     } catch (Throwable t) {
       Throwable unwrapped = unwrapThrowable(t);
       if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
         // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
         closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
         sqlSession = null;
         Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
         if (translated != null) {
           unwrapped = translated;
         }
       }
       throw unwrapped;
     } finally {
       if (sqlSession != null) {
         closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
       }
     }
   }
 }


这里invoke方法会先获取一个sqlsession,然后反射调用目标方法,最后关闭sqlsession后将result返回。我们接下来看一下其获取sqlsession的过程。


③ 获取sqlSession

这里是真实获取一个sqlsession实例对象,SqlSessionUtils.getSqlSession如下所示:

 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
//断言sessionFactory不为null
    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
 //断言executorType不为null
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
//获取SqlSessionHolder  先尝试从holder中获取SqlSession 
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
//如果获取的session不为null,则直接返回
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Creating a new SqlSession");
    }
//如果上面没有获取到sqlsession,则使用sessionFactory创建一个session 
    session = sessionFactory.openSession(executorType);
//获取一个SessionHolder,并把括号内参数赋予给SessionHolder
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
//返回获取的sqlsession
    return session;
  }


3.1 sessionFactory.openSession

DefaultSqlSessionFactory.openSession如下所示:

public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, null, false);
  }

继续往下跟踪openSessionFromDataSource:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//根据MybatisConfiguration获取environment :其有数据源、transactionFactory等
final Environment environment = configuration.getEnvironment();
//获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//获取事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//获取执行器,这里很重要哦会调用interceptorChain.pluginAll
final Executor executor = configuration.newExecutor(tx, execType);
//返回一个DefaultSqlSession实例
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}


④ configuration.newExecutor

获取执行器对象并第一次调用interceptorChain.pluginAll。这里来到了Configuration.newExecutor方法处:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //根据不同ExecutorType获取不同Executor 
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //会调用插件
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }


首先会根据ExecutorType创建不同的Executor,如批处理BatchExecutor、ReuseExecutor、SimpleExecutor以及缓存处理CachingExecutor。Executor是Mybatis的核心,负责SQL动态语句的生成以及查询缓存的维护。


而BatchExecutor、ReuseExecutor、SimpleExecutor实例化的构造方法都会走到BaseExecutor构造方法,可以看到执行器Executor持有了事务对象transaction 、缓存对象localCache 、出参缓存对象localOutputParameterCache 、配置对象configuration 以及包装执行器对象wrapper -其也是Executor类型。

 protected BaseExecutor(Configuration configuration, Transaction transaction) {
   this.transaction = transaction;
   this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
   this.localCache = new PerpetualCache("LocalCache");
   this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
   this.closed = false;
   this.configuration = configuration;
   this.wrapper = this;
 }


获取sqlsession时序图如下


⑤ InterceptorChain.pluginAll

会挨个调用插件,如果该插件对当前对象感兴趣就会对其包装生成代理对象。

此时包括的插件如下图所示:

如下代码所示,会遍历拦截器然后调用每个拦截器的plugin方法,本文这里target为CachingExecutor

public Object pluginAll(Object target) {
  for (Interceptor interceptor : interceptors) {
    target = interceptor.plugin(target);
  }
  return target;
}

5.1PaginationInterceptor.plugin

如下,其只会对StatementHandler起作用。

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PaginationInterceptor extends SqlParserHandler implements Interceptor {
//...
}

plugin方法如下:

public Object plugin(Object target) {
    if (target instanceof StatementHandler) {
        return Plugin.wrap(target, this);
    }
    return target;
}

5.2SqlExplainInterceptor.plugin

(目前只支持 MYSQL-5.6.3 以上版本)如下所示,其对Executor起作用。

@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class SqlExplainInterceptor implements Interceptor {
//...
}


plugin方法如下:

public Object plugin(Object target) {
     if (target instanceof Executor) {
         return Plugin.wrap(target, this);
     }
     return target;
 }


Plugin.wrap方法,会生成一个target代理对象

public static Object wrap(Object target, Interceptor interceptor) {
//获取感兴趣的类与类的方法
  Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
  //获取目标类型,这里为class org.apache.ibatis.executor.CachingExecutor
  Class<?> type = target.getClass();
  //获取所有type.getInterfaces()且在signatureMap 中的有
  Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
  if (interfaces.length > 0) {
    return Proxy.newProxyInstance(
        type.getClassLoader(),
        interfaces,
        new Plugin(target, interceptor, signatureMap));
  }
  return target;
}


5.3PerformanceInterceptor.plugin

其对StatementHandler起作用

@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
    @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
    @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
public class PerformanceInterceptor implements Interceptor {
//...
}


plugin方法如下所示:

public Object plugin(Object target) {
     if (target instanceof StatementHandler) {
         return Plugin.wrap(target, this);
     }
     return target;
 }


5.4 OptimisticLockerInterceptor.plugin

其对Executor起作用

@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class OptimisticLockerInterceptor implements Interceptor {
//...
}
同样调用Plugin.wrap返回一个动态代理对象。
publ


同样调用Plugin.wrap返回一个动态代理对象。

public Object plugin(Object target) {
      if (target instanceof Executor) {
          return Plugin.wrap(target, this);
      }
      return target;
  }

这时target如下所示:



插件会产生目标对象的代理对象,多个插件就会产生多层代理。创建动态代理的时候,是按照插件配置顺序创建层层代理对象。执行目标方法的之后,按照逆向顺序执行。


⑥ 依次返回到SqlSessionUtils.getSqlSession处

这里会根据session, executorType, exceptionTranslator实例化一个SqlSessionHolder


然后会依次返回,返回到③获取sqlsession处:


那么接下来就该Object result = method.invoke(sqlSession, args)使用刚获取到的sqlsession以及参数反射调用具体方法了。这里method为public abstract java.util.List org.apache.ibatis.session.SqlSession.selectList(java.lang.String,java.lang.Object,org.apache.ibatis.session.RowBounds)


参数如下图所示:


【4】查询执行流程分析之查询过程

① DefaultSqlSession.selectList

这里的statement其实就是方法的完整描述,如com.jane.mp.mapper.EmployeeMapper.selectPage,RowBounds 是用来进行分页的。这里的executor是org.apache.ibatis.executor.CachingExecutor@433ffad1

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

需要注意的是,这里的executor为代理对象:


② Plugin.invoke

代码如下所示:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
  //获取感兴趣的方法
    Set<Method> methods = signatureMap.get(method.getDeclaringClass());
    if (methods != null && methods.contains(method)) {
      return interceptor.intercept(new Invocation(target, method, args));
    }
    return method.invoke(target, args);
  } catch (Exception e) {
    throw ExceptionUtil.unwrapThrowable(e);
  }
}

如下图所示,这里target为代理对象,interceptor为OptimisticLockerInterceptor。



Executor会被OptimisticLockerInterceptor、SqlExplainInterceptor处理(二者都处理update方法),所以会进行两次代理。那么在这里也同样会进行两次解析。第二次来到invoke方法如下图所示:

如上图所示,这里我们的目标Executor是CachingExecutor。


③ CachingExecutor.query

代码如下所示:

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
   BoundSql boundSql = ms.getBoundSql(parameterObject);
   CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
   return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
 }

首先使用ms.getBoundSql获取了boundSql对象如下:


createCacheKey(ms, parameterObject, rowBounds, boundSql)创建缓存key。这个方法很有意思,它自己并不处理而是使用了其委派对象delegate去处理(这里可以联想一下设计模式的委派模式,也可以理解为这里运用了装饰器,在delegate基础功能上增加了额外功能),如下所示:

public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
   return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
 }

我们再回顾下CachingExecutor的构造方法:

public class CachingExecutor implements Executor {
//委派实例对象
  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();
  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }
  //...
  }

然后我们依次返回查看获取到的CacheKey-1113517040:-1225312459:com.jane.mp.mapper.EmployeeMapper.selectPage:0:1:SELECT id AS id,last_name AS lastName,email,gender,age,version FROM tbl_employee:MybatisSqlSessionFactoryBean

④ CachingExecutor.query

这里会首先判断缓存是否存在,如果缓存存在则尝试从缓存里面根据CacheKey 获取结果,如果结果获取不到则执行查询并再次放到缓存里面;如果缓存不存在则直接直接查询。

 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
      //获取缓存,首先从缓存里面获取,如果缓存获取不到则执行查询并放到缓存里面
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

这里delegate为SimpleExecutor实例对象,这个方法是典型的装饰器运用。

⑤ BaseExecutor.query

SimpleExecutor会调用父类BaseExecutor的query方法:

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
      //从数据库查询结果
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

⑥ BaseExecutor.queryFromDatabase

  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
    //执行查询
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

方法解释如下:


① 往localCache中放入(key, EXECUTION_PLACEHOLDER);

② doQuery执行查询获取list结果;

③ localCache.removeObject(key);从localCache中移除key

④ localCache.putObject(key, list);放入结果

⑤ 如果是存储过程则处理out类型参数localOutputParameterCache.putObject(key, parameter);

⑥ 返回结果list

⑦ SimpleExecutor.doQuery

继续看一下doQuery方法,其实这里就到了JDBC的范畴。

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
    //获取环境配置信息
      Configuration configuration = ms.getConfiguration();
      //获取语句处理器
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //准备statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 使用StatementHandler 进行查询
      return handler.<E>query(stmt, resultHandler);
    } finally {
    // 关闭Statement
      closeStatement(stmt);
    }
  }


下面我们看一下其获取StatementHandler 并使用StatementHandler 进行查询的过程。

⑧ 获取StatementHandler

需要注意的是newStatementHandler中会调用interceptorChain.pluginAll方法。那么此时如果项目配置了分页插件和性能分析插件,就会生效。

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
   StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
   statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
   return statementHandler;
 }

StatementHandler:处理sql语句预编译,设置参数等相关工作;

ParameterHandler:设置预编译参数用的

ResultHandler:处理结果集

TypeHandler:在整个过程中,进行数据库类型和javaBean类型的映射。

这里interceptorChain.pluginAll我们就不再看了,着重分析RoutingStatementHandler的创建。


如下所示其根据statementType(STATEMENT/PREPARED/CALLABLE)分别实例化SimpleStatementHandler、PreparedStatementHandler以及CallableStatementHandler。通常我们默认的是PreparedStatementHandler。

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }
  }

通常我们默认的是PreparedStatementHandler,,而PreparedStatementHandler又调用了父类BaseStatementHandler构造函数进行初始化。

// PreparedStatementHandler
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
// BaseStatementHandler
 protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
   this.configuration = mappedStatement.getConfiguration();
   this.executor = executor;
   this.mappedStatement = mappedStatement;
   this.rowBounds = rowBounds;
   this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
   this.objectFactory = configuration.getObjectFactory();
   if (boundSql == null) { // issue #435, get the key before calculating the statement
     generateKeys(parameterObject);
     boundSql = mappedStatement.getBoundSql(parameterObject);
   }
   this.boundSql = boundSql;
// 哈哈,没想到吧,在获取StatementHandler中实例化parameterHandler 、resultSetHandler 
   this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
   this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
 }

如上所示在父类BaseStatementHandler的构造函数中为configuration 、executor、mappedStatement、rowBounds、typeHandlerRegistry、objectFactory、boundSql、parameterHandler以及resultSetHandler进行了赋值。这里需要注意的是configuration.newParameterHandler和configuration.newResultSetHandler在实例化过程中同样会走一遍interceptorChain.pluginAll哦。


⑨ prepareStatement

什么意思呢?预先准备Statement。方法如下所示:获取Connection ,调用handler的prepare方法,然后进行参数化。

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
   Statement stmt;
   Connection connection = getConnection(statementLog);
   stmt = handler.prepare(connection, transaction.getTimeout());
   handler.parameterize(stmt);
   return stmt;
 }

① handler.prepare

其最终会走到抽象父类BaseStatementHandlerprepare方法,本文这里会获取一个PreparedStatement实例。

@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  ErrorContext.instance().sql(boundSql.getSql());
  Statement statement = null;
  try {
    // 实例化Statement 
    statement = instantiateStatement(connection);
    // 设置Timeout
    setStatementTimeout(statement, transactionTimeout);
    // 设置FetchSize
    setFetchSize(statement);
    return statement;
  } catch (SQLException e) {
    closeStatement(statement);
    throw e;
  } catch (Exception e) {
    closeStatement(statement);
    throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
  }
}
② handler.parameterize
PreparedStatementHandler.parameterize如下所示:
parameterHandler.setParameters((PreparedStatement) statement);

② handler.parameterize

PreparedStatementHandler.parameterize如下所示:

parameterHandler.setParameters((PreparedStatement) statement);


简单来说,就是为PreparedStatement设置参数。

@Override
public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings != null) {
    for (int i = 0; i < parameterMappings.size(); i++) {
      ParameterMapping parameterMapping = parameterMappings.get(i);
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } catch (TypeException | SQLException e) {
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        }
      }
    }
  }
}

⑩ handler.query(stmt, resultHandler)


本文这里会走到PreparedStatementHandler.query方法,简单来说就是调用PreparedStatement 执行查询,然后使用resultSetHandler对返回结果进行处理。

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  // 这里Java只是提供了接口,具体实现由各数据库厂商来定
  ps.execute();
  return resultSetHandler.handleResultSets(ps);
}

这里我们可以放一段查询流程如下(从里到外的查询流程):

at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)
at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:370)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:59)
at com.sun.proxy.$Proxy141.execute(Unknown Source)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:63)
at com.sun.proxy.$Proxy140.query(Unknown Source)
at com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.doQuery(MybatisSimpleExecutor.java:67)
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324)
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:136)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:76)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:426)

(11)resultSetHandler.handleResultSets

也就是处理返回结果集获取list。看到resultSet是不是联想到了最初写原生JDBC时的场景?原始jdbc使用resultSet解析返回结果如下所示:

ResultSetMetaData rsmd = resultSet.getMetaData();
Map<String, Object> valueMap = new HashMap<String, Object>();
/* 得到列的别名及值 */
while (resultSet.next()) {
  /* 打印每一列的列名---getcolumncount 得到結果集中多少列 */
  for (int i = 0; i < rsmd.getColumnCount(); i++) {
    /* 索引默认从 1 开始,获取列名 */
    String columnLabel = rsmd.getColumnLabel(i + 1);
    /* 获取对应的列值 */
    Object columnValue = resultSet.getObject(columnLabel);
    // Object columnValue = resultSet.getObject(i+1);
    /* 放入map里面 */
    valueMap.put(columnLabel, columnValue);
  }
}


ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商实现。ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行。


这里我们看下mybatis处理返回结果集。

@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
  final List<Object> multipleResults = new ArrayList<>();
  int resultSetCount = 0;
  ResultSetWrapper rsw = getFirstResultSet(stmt);
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  validateResultMapsCount(rsw, resultMapCount);
  while (rsw != null && resultMapCount > resultSetCount) {
    ResultMap resultMap = resultMaps.get(resultSetCount);
    handleResultSet(rsw, resultMap, multipleResults, null);
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }
  String[] resultSets = mappedStatement.getResultSets();
  if (resultSets != null) {
    while (rsw != null && resultSetCount < resultSets.length) {
      ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
      if (parentMapping != null) {
        String nestedResultMapId = parentMapping.getNestedResultMapId();
        ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
        handleResultSet(rsw, resultMap, null, parentMapping);
      }
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
  }
  return collapseSingleResultList(multipleResults);
}

SpringBoot+MybatisPlus环境下查询流程痕迹(假设查询是从UserController.checkLogin(UserController.java:306)开始)

UserController.checkLogin(UserController.java:306)
JdkDynamicAopProxy.invoke
AopUtils.invokeJoinpointUsingReflection
com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.list
com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke
com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute
com.baomidou.mybatisplus.core.override.MybatisMapperMethod.executeForMany
org.mybatis.spring.SqlSessionTemplate.selectList
org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList
org.apache.ibatis.session.defaults.DefaultSqlSession.selectList
org.apache.ibatis.executor.BaseExecutor.query
org.apache.ibatis.executor.BaseExecutor.queryFromDatabase
com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.doQuery
org.apache.ibatis.plugin.Plugin.invoke
org.apache.ibatis.executor.statement.RoutingStatementHandler.query
org.apache.ibatis.executor.statement.PreparedStatementHandler.query
org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke
com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute
com.zaxxer.hikari.pool.ProxyPreparedStatement.execute
com.mysql.cj.jdbc.ClientPreparedStatement.execute
com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal
...


这里第一步就是AopProxy,说明这里List<SysUser> list = userService.list(queryWrapper);中userService是个代理类。

com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.list时这里的baseMapper如下也是一个代理

MybatisMapperMethod.executeForMany时,这里的sqlSession其实是SqlSessionTemplate。将会调用内部的this.sqlSessionProxy.selectList(statement, parameter);方法,委派给sqlSessionProxy处理。sqlSessionProxy中的目标处理器是SqlSessionTemplate$SqlSessionInterceptor。

在SqlSessionInterceptor的invoke方法中获取真正的sqlsession处理查询。

目录
相关文章
|
1月前
|
SQL Java 数据库连接
深入 MyBatis-Plus 插件:解锁高级数据库功能
Mybatis-Plus 提供了丰富的插件机制,这些插件可以帮助开发者更方便地扩展 Mybatis 的功能,提升开发效率、优化性能和实现一些常用的功能。
197 26
深入 MyBatis-Plus 插件:解锁高级数据库功能
|
29天前
|
SQL Java 数据库连接
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。本文讲解了最新版MP的使用教程,包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段等核心功能。
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
|
1月前
|
SQL Java 数据库连接
Mybatis架构原理和机制,图文详解版,超详细!
MyBatis 是 Java 生态中非常著名的一款 ORM 框架,在一线互联网大厂中应用广泛,Mybatis已经成为了一个必会框架。本文详细解析了MyBatis的架构原理与机制,帮助读者全面提升对MyBatis的理解和应用能力。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Mybatis架构原理和机制,图文详解版,超详细!
|
13天前
|
缓存 Java 数据库连接
MyBatis缓存机制
MyBatis提供两级缓存机制:一级缓存(Local Cache)默认开启,作用范围为SqlSession,重复查询时直接从缓存读取;二级缓存(Second Level Cache)需手动开启,作用于Mapper级别,支持跨SqlSession共享数据,减少数据库访问,提升性能。
24 1
|
17天前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
34 4
|
1月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
2月前
|
SQL XML Java
Mybatis的原理和MybaitsPlus
这篇文章对比分析了Mybatis和Mybatis Plus的特点与底层实现机制,探讨了两者之间的差异及各自的优势。
86 0
|
2月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
142 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
2月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
70 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
2月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
490 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个