源码通透-mybatis源码分析以及整合spring过程

源码通透-mybatis源码分析以及整合spring过程


mybatis源码分析版本:mybaits3 (3.5.0-SNAPSHOT)

mybatis源码下载地址:https://github.com/mybatis/mybatis-3

mybatis整合spring的jar版本:mybatis-spring-1.3.2-sources.jar,后使用2.0.0-SNAPSHOT

spring源码版本: 4.2.0.RELEASE

其它相关源码分析文章:https://github.com/arthur-dy-lee/arthur-dy-lee-note

文章目录

  • 源码通透-mybatis源码分析以及整合spring过程
  • 一、启动时注入spring
    • 1.1 springmvc的xml配置方式
    • 1.2 SqlSessionFactoryBean 创建mybatis的SqlSessionFactory。并将*mapper.xml, dataSource注入到SqlSessionFactory中
      • 1.2.1 SqlSessionFactoryBean作用
      • 1.2.2 SqlSessionFactoryBean的继承关系
      • 1.2.3 SqlSessionFactoryBean#getObject
      • 1.2.4 afterPropertiesSet源码
      • 1.2.5 创建DefaultSqlSessionFactory并返回SqlSessionFactory
    • 1.3 *Mapper.xml解析 XMLMapperBuilder#parse
      • 1.3.1 XMLMapperBuilder#configurationElement
      • 1.3.2 XMLLanguageDriver
      • 1.3.3 XMLMapperBuilder#bindMapperForNamespace
      • 1.3.4 configuration#addMapper#
    • 1.4 通过MapperScannerConfigurer,将*mapper.java注入到spring容器中
      • 1.4.1 MapperScannerConfigurer作用
      • 1.4.2 MapperScannerConfigurer继承关系
      • 1.4.3 postProcessBeanDefinitionRegistry源码
        • ClassPathMapperScanner#processBeanDefinitions作用是重新组装BeanDefinitions
    • 1.5 调用MapperFactoryBean#getObject
      • 1.5.1 `MapperFactoryBean`的继承结构
      • 1.5.2 SqlSessionTemplate初始化
      • 1.5.3 实例化栈信息打印(创建MapperFactoryBean)
      • 1.5.4 初始化栈信息,即调用MapperFactoryBean#getObject的栈过程
      • 1.5.5 getObject内容分析
  • 二、调用前的拦截器工作
    • 2.1 简介做准备工作
    • 2.2 SqlSessionTemplate创建时,加入拦截器SqlSessionInterceptor
    • 2.3 SqlSessionIntercepteor
    • 2.3 SqlSession
      • 2.3.1 SqlSession类继承结构
      • 2.3.2 DefaultSqlSession
      • 2.3.3 SqlSessionUtils#getSqlSession
    • 2.3 DefaultSqlSessionFactory创建事务、SqlSession、Executor
    • 2.4 Executor执行器介绍
      • 2.4.1 SimpleExecutor类结构
  • 三、调用过程详解
    • 3.1 原生mybatis创建和执行SqlSession的流程示例
    • 3.2 spring调用mybatis过程栈打印
    • 3.3 MapperProxy 代理Mapper执行
    • 3.4 MapperMethod#execute
      • 3.4.1 MapperMethod#execute
      • 3.4.2 executeForMany
    • 3.5 SqlSessionTemplate#selectList
    • 3.5 SqlSessionInterceptor#invoke
    • 3.7 DefaultSqlSession#selectList
    • 3.8 CachingExecutor#query
    • 3.9 SimpleExecutor
      • 3.9.1 BaseExecutor#query
      • 3.9.2 BaseExecutor#queryFromDatabase
      • 3.9.3 SimpleExecutor#doQuery
      • 3.9.4 SimpleExecutor#prepareStatement
    • 3.10 RoutingStatementHandler#query
    • 3.11 PreparedStatementHandler#query
  • 四、MyBatis核心构件
  • 五、事务
    • 5.1 mybatis-spring的Transaction准备阶段
    • 5.2 mybatis-spring创建SqlSession,并交由spring去得到connection
      • 5.2.2 openSession 时创建mybatis事务
      • 5.2.3 保存前当sqlSession,创建并注册事务同步器。
      • 5.2.4 SpringManagedTransaction.getConnection()取出当前connection
        • SpringManagedTransaction#openConnection
      • 5.2.5 交由spring根据dataSource去创建Connection
    • 5.3 spring事务调用
      • 5.3.1 执行过程摘要
        • **invokeWithinTransaction**
      • 5.3.2 执行目标方法(`Service.*methoad()`)前的CGLIB代理拦截器调用
        • 创建`PlatformTransactionManager(DataSourceTransactionManager)`,并为`DataSourceTransactionManager`的属性`dataSoucre`赋值
        • CGLIB代理拦截器栈调用过程
      • 5.3.3 执行目标方法(`Service.*methoad()`)
      • 5.3.4 执行目标方法(`Service.*methoad()`)后的CGLIB代理拦截器调用:commit提交
        • commit真正提交栈信息打印
        • DataSourceTransactionManager#doCommit 真正提交
        • 真正提交前的预提交
          • TransactionSynchronizationUtils#triggerBeforeCommit
          • TransactionSynchronizationUtils#triggerBeforeCompletion
  • 六 spring事务调用总结
    • 6.1 调用
  • 七、 插一段初始化执行顺序
  • 八、其它补充
    • 8.1 Mapper接口对应的bean在容器中的数量

一、启动时注入spring


1.1 springmvc的xml配置方式

在springmvc中我们常这样配置


<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mapperLocations" value="classpath*:com/paincupid/springmvc/persistence/*.xml" />
    <property name="typeAliasesPackage" value="com.paincupid.springmvc.domain" />
bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.paincupid.springmvc.persistence" />
bean>

可以通过mybatis-spring-1.3.2-sources.jar下载源码看2个类:

org.mybatis.spring.SqlSessionFactoryBean

SqlSessionFactoryBean负责创建mybatis的SqlSessionFactory。并将*mapper.xml, dataSource注入到SqlSessionFactory中。

org.mybatis.spring.mapper.MapperScannerConfigurer

MapperScannerConfigurer负责扫描装载dal的java文件到spring容器中。

1.2 SqlSessionFactoryBean 创建mybatis的SqlSessionFactory。并将*mapper.xml, dataSource注入到SqlSessionFactory中

在基本的(纯的,不与spring整合的) MyBatis 中,session 工厂可以使用 SqlSessionFactoryBuilder 来创建。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来替代。

1.2.1 SqlSessionFactoryBean作用

{@code FactoryBean} that creates an MyBatis {@code SqlSessionFactory}.
This is the usual way to set up a shared MyBatis {@code SqlSessionFactory} in a Spring application context;
the SqlSessionFactory can then be passed to MyBatis-based DAOs via dependency injection. Either {@code DataSourceTransactionManager} or {@code JtaTransactionManager} can be used for transaction demarcation in combination with a {@code SqlSessionFactory}. JTA should be used for transactions which span multiple databases or when container managed transactions (CMT) are being used.

FactoryBean(SqlSessionFactoryBean)创建mybatis的SqlSessionFactory,在spring上下文中,通常用它来装载mybatis;SqlSessionFactory能够通过依赖注入的方式,传到MyBatis的dao中。同时,DataSourceTransactionManager或者JtaTransactionManager与SqlSessionFactory组合进行事务管理。JTA应该用于跨多个数据库的事务或使用容器管理事务(CMT) 。

1.2.2 SqlSessionFactoryBean的继承关系

源码通透-mybatis源码分析以及整合spring过程_第1张图片

1、实现FactoryBean接口,FactoryBean用于创建复杂对象时使用,通过getObject对外提供SqlSessionFactory实例。

2、实现InitializingBean,在spring启动初始化时执行afterPropertiesSet()方法。

1.2.3 SqlSessionFactoryBean#getObject

在一般的 MyBatis-Spring 用法中, 你不需要直接使用 SqlSessionFactoryBean 或和其对应的 SqlSessionFactory。相反,session 工厂将会被注入到 MapperFactoryBean 或其它扩 展了 SqlSessionDaoSupport 的 DAO中。

通过FacotryBean.getObject提供DefaultSqlSessionFactory对象实例

返回之前创建的DefaultSqlSessionFactory,这里可以看到DefaultSqlSessionFactory只会被初始化一次

SqlSessionFactoryBean#getObject

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

  return this.sqlSessionFactory;
}

1.2.4 afterPropertiesSet源码

初始化sqlSessionFactory,相当于,先组装和注册configuration,将configuration传给sqlSessionFactory :

sqlSessionFactory = new DefaultSqlSessionFactory(configuration);

@Override
public void afterPropertiesSet() throws Exception {
  notNull(dataSource, "Property 'dataSource' is required");
  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();
}

buildSqlSessionFactory

/**
* Build a {@code SqlSessionFactory} instance.
*
* The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
* {@code SqlSessionFactory} instance based on an Reader.
* Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
*
* @return SqlSessionFactory
* @throws IOException if loading the config file failed
*/
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

Configuration configuration;

XMLConfigBuilder 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) {
  xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
  configuration = xmlConfigBuilder.getConfiguration();
} 
//为configuration的属性赋值以及注册一些东西.....


if (xmlConfigBuilder != null) {
  try {
	//解析xml文件  
	xmlConfigBuilder.parse();
	//....	
}

if (this.transactionFactory == null) {
  this.transactionFactory = new SpringManagedTransactionFactory();
}

configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

//
//解析*mapper.xml
if (!isEmpty(this.mapperLocations)) {
  for (Resource mapperLocation : this.mapperLocations) {
	if (mapperLocation == null) {
	  continue;
	}

	try {
      //XMLMapperBuilder解析*Mapper.xml文件中的标签...  
	  XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
		  configuration, mapperLocation.toString(), configuration.getSqlFragments());
	  xmlMapperBuilder.parse();
	//....
  }
}

return this.sqlSessionFactoryBuilder.build(configuration);
}

1.2.5 创建DefaultSqlSessionFactory并返回SqlSessionFactory

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

1.3 *Mapper.xml解析 XMLMapperBuilder#parse

XMLMapperBuilder#parse 入口类

public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    configurationElement(parser.evalNode("/mapper")); //<----- 1.3.1
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();//<----- 1.3.3
  }

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

1.3.1 XMLMapperBuilder#configurationElement

XMLMapperBuilder#configurationElement

private void configurationElement(XNode context) {
  try {
    String namespace = context.getStringAttribute("namespace"); //得到namespace
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    cacheRefElement(context.evalNode("cache-ref"));
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    //处理这类标签,然后再MapperBuilderAssistant类的addResultMap()方法中把每个ResultMap对象加到Configuration对象中的resultMaps属性中(中间会把标签中的每一个子标签封装成ResultMapping对象,然后封装成ResultMap对象,最后put到Configuration对象中,id规则为:namespace+的id属性 )
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));//<--------
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
  }
}

XMLMapperBuilder#buildStatementFromContext

private void buildStatementFromContext(List<XNode> list) {
  if (configuration.getDatabaseId() != null) {
    buildStatementFromContext(list, configuration.getDatabaseId());
  }
  buildStatementFromContext(list, null);
}

XMLMapperBuilder#buildStatementFromContext

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  for (XNode context : list) {
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteStatement(statementParser);
    }
  }
}

XMLStatementBuilder#parseStatementNode 解析标签

public void parseStatementNode() {
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }

  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  String parameterType = context.getStringAttribute("parameterType");
  Class<?> parameterTypeClass = resolveClass(parameterType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultType = context.getStringAttribute("resultType");
  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  Class<?> resultTypeClass = resolveClass(resultType);
  String resultSetType = context.getStringAttribute("resultSetType");
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

  // Include Fragments before parsing
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver);
  
  // <----------------------  
  // Parse the SQL (pre:  and  were parsed and removed)
  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  String resultSets = context.getStringAttribute("resultSets");
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  KeyGenerator keyGenerator;
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }

  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered, 
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

下面看:

SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

1.3.2 XMLLanguageDriver

XMLLanguageDriver#createSqlSource

@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
  XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
  return builder.parseScriptNode();
}

XMLLanguageDriver#parseScriptNode

public SqlSource parseScriptNode() {
  //解析动态标签比如if、foreach...每个动态标签都会有不同的handel去处理,如果动态标签嵌套动态标签的话,还会递归去调用parseDynamicTags(XNode node)方法,每一个标签封装成一个sqlNode  
  List<SqlNode> contents = parseDynamicTags(context);
  //封装一个MixedSqlNode对象,有一个apply方法,用户解析动态sql  
  MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
  SqlSource sqlSource = null;
  if (isDynamic) {
    sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
  } else {
    sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
  }
  return sqlSource;
}

到这里整个configurationElement(XNode context)方法就分析完了。

1.3.3 XMLMapperBuilder#bindMapperForNamespace

private void bindMapperForNamespace() {
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      //ignore, bound type is not required
    }
    if (boundType != null) {
      if (!configuration.hasMapper(boundType)) {
        // Spring may not know the real resource name so we set a flag
        // to prevent loading again this resource from the mapper interface
        // look at MapperAnnotationBuilder#loadXmlResource
        configuration.addLoadedResource("namespace:" + namespace);
        configuration.addMapper(boundType);
      }
    }
  }
}

看代码configuration.addMapper(boundType);

1.3.4 configuration#addMapper#

将MapperProxyFactory放到knownMappers,后面会通过MapperRegistry#getMapper重新取出来,可以看1.5.5 getObject时取。knownMappers里面放的都是自己工程中的*Mapper.xml

public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}

MapperRegistry#addMapper

public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      // 将MapperProxyFactory放到knownMappers,后面会通过MapperRegistry#getMapper重新取出来,可以看1.5.5 getObject时取。
      knownMappers.put(type, new MapperProxyFactory<T>(type)); 
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

1.4 通过MapperScannerConfigurer,将*mapper.java注入到spring容器中

1.4.1 MapperScannerConfigurer作用

将*mapper.java注入到spring容器中,在组装mapper的BeanDefinition的时候,会将sqlSessionFactorysqlSessionTemplate一起放到BeanDefinition中。

1.4.2 MapperScannerConfigurer继承关系

源码通透-mybatis源码分析以及整合spring过程_第2张图片

1、实现postProcessBeanDefinitionRegistry接口,它实际是一个BeanFactoryPostProcessor,它会调用BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法。

2、实现InitializingBean,调用afterPropertiesSet(),在MapperScannerConfigurer中,只是用它用做校验

@Override
public void afterPropertiesSet() throws Exception {
  notNull(this.basePackage, "Property 'basePackage' is required");
}

3、实现ApplicationContextAware接口,执行ApplicationContextAware.setApplicationContext(ApplicationContext applicationContext)`方法。用于将applicationContext传过来。

@Override
public void setApplicationContext(ApplicationContext applicationContext) {
  this.applicationContext = applicationContext;
}

4、实现BeanNameAware 接口,为bean命名

@Override
public void setBeanName(String name) {
    this.beanName = name;
}

这里主要看postProcessBeanDefinitionRegistry方法

1.4.3 postProcessBeanDefinitionRegistry源码

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  if (this.processPropertyPlaceHolders) {
    processPropertyPlaceHolders();
  }

  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.registerFilters();
  //<------  
  scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

ClassPathMapperScanner#scan

public int scan(String... basePackages) {
   int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

   doScan(basePackages);

   // Register annotation config processors, if necessary.
   //如果有注解,也一起注册到容器中 
   if (this.includeAnnotationConfig) {
      AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
   }

   return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

ClassPathMapperScanner#doScan

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  //会调用父类的ClassPathBeanDefinitionScanner。doScan方法 <-----
  Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

  if (beanDefinitions.isEmpty()) {
    logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
  } else {
    //<-----
    processBeanDefinitions(beanDefinitions);
  }
  return beanDefinitions;
}

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScannerspring-context包下的类,直接使用spring的类去进行*mapper.java的注册。
ClassPathBeanDefinitionScanner的包全路径为:
org.springframework.context.annotation.ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner#doScan源码

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
   for (String basePackage : basePackages) {
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}

这里只看一下doScan()的结果。Set beanDefinitions断点,我们可以看到包扫描得的mapper,当然现在还是beanDefinition的形式。

源码通透-mybatis源码分析以及整合spring过程_第3张图片

processBeanDefinitions方法

ClassPathMapperScanner#processBeanDefinitions作用是重新组装BeanDefinitions

1、将MapperFactoryBean放到beanDefinitions。将beanDefinitionsbeanClass设置为MapperFactoryBean。mapper接口是原始的bean,但MapperFactoryBean是真正的bean。也就是说注册到spring容器中的是MapperFactoryBean

the mapper interface is the original class of the bean. but, the actual class of the bean is MapperFactoryBean

2、将sqlSessionFactory放到beanDefinitions

3、将sqlSessionTemplate放到beanDefinitions

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  GenericBeanDefinition definition;
  for (BeanDefinitionHolder holder : beanDefinitions) {
    definition = (GenericBeanDefinition) holder.getBeanDefinition();

    // the mapper interface is the original class of the bean
    // but, the actual class of the bean is MapperFactoryBean 
    definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
    //实际类为 MapperFactoryBean  <------------
    definition.setBeanClass(this.mapperFactoryBean.getClass());

    definition.getPropertyValues().add("addToConfig", this.addToConfig);

    boolean explicitFactoryUsed = false;
    if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
      //注册sqlSessionFactory <---------    
      definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
      explicitFactoryUsed = true;
    } else if (this.sqlSessionFactory != null) {
      //注册sqlSessionFactory <---------  
      definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
      explicitFactoryUsed = true;
    }

    if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
      //注册 sqlSessionTemplate <-------------  
      definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
      explicitFactoryUsed = true;
    } else if (this.sqlSessionTemplate != null) {
      //注册 sqlSessionTemplate <-------------
      definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
      explicitFactoryUsed = true;
    }

    if (!explicitFactoryUsed) {
      //将beanDefinition设为按类型注入
      definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    }
  }
}

1.5 调用MapperFactoryBean#getObject

在将**Mapper变成MapperFactoryBean组装成beanDefinition注册到容器beanFactory容器后,会继续执行spring的refresh()中的finishBeanFactoryInitialization方法,此方法的作用是:完成spring上下文的初始化,初始化beanFactory剩余的单例bean。这时,会实例化MapperFactoryBean

1.5.1 MapperFactoryBean的继承结构

MapperFactoryBean也是一个FactoryBean,并且间接实现了InitializingBean接口。

DaoSupport.afterPropertiesSet()

此处用模板板式,2个抽象方法:checkDaoConfig()initDao(),先执行checkDaoConfig(),后执行initDao()

@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
   // Let abstract subclasses check their configuration.
   checkDaoConfig();

   // Let concrete implementations initialize themselves.
   try {
      initDao();
   }
   catch (Exception ex) {
      throw new BeanInitializationException("Initialization of DAO failed", ex);
   }
}
protected abstract void checkDaoConfig() throws IllegalArgumentException;

protected void initDao() throws Exception {}

1.5.2 SqlSessionTemplate初始化

SqlSessionTemplate,是spring连接mybatis的模板类,是由spring负责生成的

MapperFactoryBean继承SqlSessionDaoSupportSqlSessionDaoSupport使用构造函数,创建SqlSessionTemplate对象,并将它保存到sqlSession属性中。

public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
  if (!this.externalSqlSession) {
    this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
  }
}

SqlSessionTemplate 3个构造函数调用过程

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
  this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
  this(sqlSessionFactory, executorType,
      new MyBatisExceptionTranslator(
          sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}

在SqlSessionTemplate初始化的时候,就将拦截器SqlSessionInterceptor放进去。

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());
}

1.5.3 实例化栈信息打印(创建MapperFactoryBean)

如下图所示:

源码通透-mybatis源码分析以及整合spring过程_第4张图片

可以看到最后是通过反射,调用构造函数来实现MapperFactoryBean的实例化。

看一下java.lang.reflect.Constructor.newInstance()

public T newInstance(Object ... initargs)
    throws InstantiationException, IllegalAccessException,
           IllegalArgumentException, InvocationTargetException
{
    //......
    ConstructorAccessor ca = constructorAccessor;   // read volatile
    if (ca == null) {
        ca = acquireConstructorAccessor();
    }
    @SuppressWarnings("unchecked")
    T inst = (T) ca.newInstance(initargs);
    return inst;
}

这些执行步骤只是用反射把对象new出来,但还没有完成对象实例,下面还会进行对象的初始化。

1.5.4 初始化栈信息,即调用MapperFactoryBean#getObject的栈过程

public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface); //getSqlSession()是SqlSessionTemplate
}

备注:getSqlSession()SqlSessionTemplate, 上面已讲了SqlSessionTemplate的初始化。

newInstance:47, MapperProxyFactory (org.apache.ibatis.binding)
newInstance:52, MapperProxyFactory (org.apache.ibatis.binding)
getMapper:50, MapperRegistry (org.apache.ibatis.binding)
getMapper:732, Configuration (org.apache.ibatis.session)
getMapper:318, SqlSessionTemplate (org.mybatis.spring)
getObject:95, MapperFactoryBean (org.mybatis.spring.mapper)
doGetObjectFromFactoryBean:168, FactoryBeanRegistrySupport (org.springframework.beans.factory.support)
getObjectFromFactoryBean:103, FactoryBeanRegistrySupport (org.springframework.beans.factory.support)
getObjectForBeanInstance:1574, AbstractBeanFactory (org.springframework.beans.factory.support)
doGetBean:253, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:201, AbstractBeanFactory (org.springframework.beans.factory.support)
doGetBean:274, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:196, AbstractBeanFactory (org.springframework.beans.factory.support)
findAutowireCandidates:1145, DefaultListableBeanFactory (org.springframework.beans.factory.support)
doResolveDependency:1069, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:967, DefaultListableBeanFactory (org.springframework.beans.factory.support)
inject:543, AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement (org.springframework.beans.factory.annotation)
inject:88, InjectionMetadata (org.springframework.beans.factory.annotation)
postProcessPropertyValues:331, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
populateBean:1214, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:543, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:482, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
getObject:305, AbstractBeanFactory$1 (org.springframework.beans.factory.support)
getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:301, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:196, AbstractBeanFactory (org.springframework.beans.factory.support)
findAutowireCandidates:1145, DefaultListableBeanFactory (org.springframework.beans.factory.support)
doResolveDependency:1069, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:967, DefaultListableBeanFactory (org.springframework.beans.factory.support)
inject:543, AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement (org.springframework.beans.factory.annotation)
inject:88, InjectionMetadata (org.springframework.beans.factory.annotation)
postProcessPropertyValues:331, AutowiredAnnotationBeanPostProcessor (org.springframework.beans.factory.annotation)
populateBean:1214, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:543, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:482, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
getObject:305, AbstractBeanFactory$1 (org.springframework.beans.factory.support)
getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:301, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:196, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:772, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:834, AbstractApplicationContext (org.springframework.context.support)

在调用过程中,我们可以看一下

AbstractBeanFactory#getObjectForBeanInstance 如果是普通beanInstance或者beanName前有&符号,则返回它;如果是FactoryBean类型的实例,则返回FactoryBean.getObject。

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

   // Don't let calling code try to dereference the factory if the bean isn't a factory.
   if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
      throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
   }

   // Now we have the bean instance, which may be a normal bean or a FactoryBean.
   // If it's a FactoryBean, we use it to create a bean instance, unless the
   // caller actually wants a reference to the factory.
   // <-------------- 
   if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
      return beanInstance;
   }

   Object object = null;
   if (mbd == null) {
      object = getCachedObjectForFactoryBean(beanName);
   }
   if (object == null) {
      // Return bean instance from factory.
      FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
      // Caches object obtained from FactoryBean if it is a singleton.
      if (mbd == null && containsBeanDefinition(beanName)) {
         mbd = getMergedLocalBeanDefinition(beanName);
      }
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}

看一下BeanFactoryUtils.isFactoryDereference源码:

public static boolean isFactoryDereference(String name) {
   //String FACTORY_BEAN_PREFIX = "&"; 
   return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}

FactoryBeanRegistrySupport#doGetObjectFromFactoryBean,会调用FactoryBean.getObject方法。

/**
 * Obtain an object to expose from the given FactoryBean.
 * @param factory the FactoryBean instance
 * @param beanName the name of the bean
 * @return the object obtained from the FactoryBean
 * @throws BeanCreationException if FactoryBean object creation failed
 * @see org.springframework.beans.factory.FactoryBean#getObject()
 */
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
      throws BeanCreationException {

   Object object;
   try {
      if (System.getSecurityManager() != null) {
         AccessControlContext acc = getAccessControlContext();
         try {
            object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
               @Override
               public Object run() throws Exception {
                     return factory.getObject();
                  }
               }, acc);
         }
         catch (PrivilegedActionException pae) {
            throw pae.getException();
         }
      }
      else {
         object = factory.getObject();
      }
   }
   // ...
   return object;
}

1.5.5 getObject内容分析

public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface); //getSqlSession()是SqlSessionTemplate
}

getSqlSession()SqlSessionTemplate

是SqlSessionTemplate#getMapper

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  private final SqlSessionFactory sqlSessionFactory;
  private final ExecutorType executorType;
  private final SqlSession sqlSessionProxy;
  private final PersistenceExceptionTranslator exceptionTranslator;
    
  @Override
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }
}

getConfiguration()

@Override
public Configuration getConfiguration() {
  return this.sqlSessionFactory.getConfiguration();
}

sqlSessionFactory的值为:DefaultSqlSessionFactory

DefaultSqlSessionFactorySqlSessionFactory的实现类。前面springmvc配置xml文件中的org.mybatis.spring.SqlSessionFactoryBean,就是用来产生SqlSessionFactory,在SqlSessionFactoryBean。getObject()的时候,就会返回一个DefaultSqlSessionFactory`。

Configuration#getMapper

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

MapperRegistry#getMapper

public class MapperRegistry {
  private final Configuration config;
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }
    
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
      if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
      }
      try {
        return mapperProxyFactory.newInstance(sqlSession);
      } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
      }
    }
    
}

knownMappers会缓存所有的 *Mapper。此时knownMappers, size = 3里面存的是:

源码通透-mybatis源码分析以及整合spring过程_第5张图片

MapperProxyFactory#newInstance 通过代理工厂来创建实例。

protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}

此处 mapperInterface = interface com.paincupid.springmvc.persistence.PersonMapper

后由MapperProxy代理*Mapper执行,上面返回Mapper的代理类实例mapperProxy,在执行时,通过JDK来执行MapperProxy.invoke方法。最后getObject得到的是MapperProxy

二、调用前的拦截器工作


2.1 简介做准备工作

1、SqlSessionInterceptor代理了SqlSessionTemplate。在创建MapperFactoryBean时,通过构造函数就会创建SqlSessionInterceptor,所以在执行SqlSessionTemplate时,会被SqlSessionInterceptor拦截,然后执行时,真正是通过DefaultSqlSession执行。

2、DefaultSqlSession持有执行器、configuration。事务保存在执行器中,由执行器执行查询操作。

2.2 SqlSessionTemplate创建时,加入拦截器SqlSessionInterceptor

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());
}

2.3 SqlSessionIntercepteor

托管流程:spring创建了SqlSessionTemplate,在创建SqlSessionTemplate的时候提供了方法拦截器 SqlSessionInterceptor;SqlSessionTemplate实现了SqlSession接口,所以SqlSessionInterceptor会对SqlSessionTemplate中所有SqlSession接口定义的方法进行拦截;也就是说,整合spring之后的crud操作都会经过SqlSessionTemplate类,并且所有crud方法会被SqlSessionInterceptor拦截;最终SqlSessionTemplate通过代理拦截,并且通过SqlSessionHolder实现的sqlsession线程安全和自动新建和释放连接。

使用JDK动态代理,SqlSessionInterceptor实现InvocationHandler接口,在SqlSessionTemplate`类中,源码如下:

/**
 * Proxy needed to route MyBatis method calls to the proper SqlSession got
 * from Spring's Transaction Manager
 * It also unwraps exceptions thrown by {@code Method#invoke(Object, Object...)} to
 * pass a {@code PersistenceException} to the {@code PersistenceExceptionTranslator}.
 */
private class SqlSessionInterceptor implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //getSqlSession很重要,创建了DefaultSqlSession、事务、执行器。事务保存在执行器中,  DefaultSqlSession持有执行器、configuration
    SqlSession sqlSession =getSqlSession(
        SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType,
        SqlSessionTemplate.this.exceptionTranslator);
    try {
      //通过sqlSession对象执行真正的crud操作  
      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);
      }
    }
  }
}

2.3 SqlSession

2.3.1 SqlSession类继承结构

源码通透-mybatis源码分析以及整合spring过程_第6张图片

2.3.2 DefaultSqlSession

上面的getSession,得到的是DefaultSqlSession。事务保存在执行器中, DefaultSqlSession持有执行器、configuration

public class DefaultSqlSession implements SqlSession {

  private Configuration configuration;
  private Executor executor;

  private boolean autoCommit;
  private boolean dirty;
  private List<Cursor<?>> cursorList;

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }
}

2.3.3 SqlSessionUtils#getSqlSession

/**
 * Gets an SqlSession from Spring Transaction Manager or creates a new one if needed.
 * Tries to get a SqlSession out of current transaction. If there is not any, it creates a new one.
 * Then, it synchronizes the SqlSession with the transaction if Spring TX is active and
 * SpringManagedTransactionFactory is configured as a transaction manager.
 *
 * @param sessionFactory a MyBatis {@code SqlSessionFactory} to create new sessions
 * @param executorType The executor type of the SqlSession to create
 * @param exceptionTranslator Optional. Translates SqlSession.commit() exceptions to Spring exceptions.
 * @throws TransientDataAccessResourceException if a transaction is active and the
 *             {@code SqlSessionFactory} is not using a {@code SpringManagedTransactionFactory}
 * @see SpringManagedTransactionFactory
 */
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

  notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

  SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

  SqlSession session = sessionHolder(executorType, holder);
  if (session != null) {
    return session;
  }

  if (LOGGER.isDebugEnabled()) {
    LOGGER.debug("Creating a new SqlSession");
  }

  session = sessionFactory.openSession(executorType); //new DefaultSqlSession <-----

  registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

  return session;
}

2.3 DefaultSqlSessionFactory创建事务、SqlSession、Executor

在执行openSessionFromDataSource()时,会创建

1、执行器Executor executor = configuration.newExecutor(tx, execType);

2、创建SqlSession:new DefaultSqlSession(configuration, executor, autoCommit);

3、创建Transaction :tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

DefaultSqlSessionFactory#openSession

openSession创建了DefaultSqlSession、事务、执行器。事务保存在执行器中, DefaultSqlSession持有执行器、configuration

@Override
public SqlSession openSession(ExecutorType execType) {
  return openSessionFromDataSource(execType, null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    //创建事务  
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    //创建执行器  
    final Executor executor = configuration.newExecutor(tx, execType);
    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(tx, execType);根据类型创建执行器。

2.4 Executor执行器介绍

Executor是MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护 。

Configuration#newExecutor 会根据ExecutorType创建不同的Executor: SimpleExecutorBatchExecutorReuseExecutor

Executor代表执行器,他来调度StatementHandler、ParameterHandler、ResultHandler等来执行SQL;

Configuration#newExecutor

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor 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) {
    //new CachingExecutor, 在本例中,executor = new SimpleExecutor 所以后面调用的是先是CachingExecutor,后是SimpleExecutor。
    executor = new CachingExecutor(executor);
  }
  //可追加拦截器  
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

在本例中,executor = new SimpleExecutor 所以后面调用的是先是CachingExecutor,后是SimpleExecutor

executor = (Executor) interceptorChain .pluginAll(executor);表明可追加拦截器。

用户可以自定义拦截器,可以实现Interceptor接口,然后封装插件,比如分页插件。

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);
  }

}

2.4.1 SimpleExecutor类结构

源码通透-mybatis源码分析以及整合spring过程_第7张图片

SimpleExecutor#doQuery

这个和传统的JDBC调用就没什么区别了,先建立链接,再创建Statement,然后再执行,最后返回结果集。

@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();
    //创建Statement  
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.<E>query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

三、调用过程详解


3.1 原生mybatis创建和执行SqlSession的流程示例

SqlSession sqlSession = null;
try {
    sqlSession = sqlSessionFactory.openSession();
    sqlSession.insert("cn.jarjar.dao.BlogMapper.insertBlog", blog);
    sqlSession.commit(true)
} catch (Exception e) {
    e.printStackTrace();
    sqlSession.rollback(true);
} finally { 
    sqlSession.close();
}

3.2 spring调用mybatis过程栈打印

栈显示的是PersionMapper.list()方法为示例的。

query:62, PreparedStatementHandler (org.apache.ibatis.executor.statement)
query:79, RoutingStatementHandler (org.apache.ibatis.executor.statement)
doQuery:63, SimpleExecutor (org.apache.ibatis.executor)
queryFromDatabase:324, BaseExecutor (org.apache.ibatis.executor)
query:156, BaseExecutor (org.apache.ibatis.executor)
query:109, CachingExecutor (org.apache.ibatis.executor)
query:83, CachingExecutor (org.apache.ibatis.executor)
selectList:148, DefaultSqlSession (org.apache.ibatis.session.defaults)
selectList:141, DefaultSqlSession (org.apache.ibatis.session.defaults)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
invoke:433, SqlSessionTemplate$SqlSessionInterceptor (org.mybatis.spring)
selectList:-1, $Proxy11 (com.sun.proxy)
selectList:230, SqlSessionTemplate (org.mybatis.spring)
executeForMany:137, MapperMethod (org.apache.ibatis.binding)
execute:75, MapperMethod (org.apache.ibatis.binding)
invoke:59, MapperProxy (org.apache.ibatis.binding)
listPerson:-1, $Proxy20 (com.sun.proxy)
listPerson:24, PersonService (com.paincupid.springmvc.service)
listPerson:35, PersonController (com.paincupid.springmvc.controller)

controller一路调用下去。PersonController调用 PersonService

1、PersonService调用 PersonMapper的时候,会调用它的代理类 MapperProxy

2、MapperProxy执行由MapperMethod执行:mapperMethod.execute(sqlSession, args);

3.3 MapperProxy 代理Mapper执行

MapperProxy 代理实例生成,在调用后加了一层缓存。

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);//将新生成的MapperMethod缓存起来
    return mapperMethod.execute(sqlSession, args);
  }    
  //...
}

MapperMethod持有 mapperInterfacemethodConfiguration

private MapperMethod cachedMapperMethod(Method method) {
  MapperMethod mapperMethod = methodCache.get(method);
  if (mapperMethod == null) {
    mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
    methodCache.put(method, mapperMethod);
  }
  return mapperMethod;
}

3.4 MapperMethod#execute

3.4.1 MapperMethod#execute

MapperMethod会调用

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:
      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;
}

3.4.2 executeForMany

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
  List<E> result;
  Object param = method.convertArgsToSqlCommandParam(args);
  if (method.hasRowBounds()) {
    RowBounds rowBounds = method.extractRowBounds(args);
    //sqlSession是SqlSessionTemplate  <----------
    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 执行,此处sqlSession ,就是上面的SqlSessionTemplate,所以会执行SqlSessionTemplate.selectList()

3.5 SqlSessionTemplate#selectList

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

调用sqlSessionProxy时,会被SqlSessionInterceptor拦截。反射执行其方法。

3.5 SqlSessionInterceptor#invoke

执行sqlSession.selectListSqlSessionTemplate.selectList)的时候 ,会被拦截器SqlSessionIntercepteor拦截,上面已经讲过。然后先getSession方法,得到的是 DefaultSqlSession

源码通透-mybatis源码分析以及整合spring过程_第8张图片

private class SqlSessionInterceptor implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    SqlSession sqlSession = getSqlSession(
        SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType,
        SqlSessionTemplate.this.exceptionTranslator);
    try {
      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;
    } //...
  }
}

getSqlSession()上面介绍过了这一步,创建了DefaultSqlSession、事务、执行器。事务保存在执行器中, DefaultSqlSession持有执行器、configuration

method.invoke代理的是DefaultSqlSession

3.7 DefaultSqlSession#selectList

DefaultSqlSession#selectList

先取出MappedStatement,然后执行查询,调用executor.query,之前的准备工作分析了,DefaultSqlSession持有执行器Executor

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

@Override
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);
  } //....
}

3.8 CachingExecutor#query

CachingExecutor#query

@Override
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);
}

CachingExecutor#query

@Override
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, parameterObject, 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代理的是自己,此处的delegate是SimpleExecutor

public class CachingExecutor implements Executor {

  private Executor delegate;
  private TransactionalCacheManager tcm = new TransactionalCacheManager();

  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this); //代理的是自己
  }
}

3.9 SimpleExecutor

3.9.1 BaseExecutor#query

SimpleExecutor执行继承自父类方法:BaseExecutor#query

@Override
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++;
    //localCache是个PerpetualCache类型的实例,PerpetualCache类是实现了MyBatis的Cache缓存接口的实现类之一,内部有个Map类型的属性用来存储缓存数据。 这个localCache就是一级缓存!  
    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;
}

localCache是个PerpetualCache类型的实例,PerpetualCache类是实现了MyBatis的Cache缓存接口的实现类之一,内部有个Map类型的属性用来存储缓存数据。 这个localCache就是一级缓存!

3.9.2 BaseExecutor#queryFromDatabase

SimpleExecutor执行继承自父类方法: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;
}

3.9.3 SimpleExecutor#doQuery

SimpleExecutor创建StatementHandler,由StatementHandler以

@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);
    stmt = prepareStatement(handler, ms.getStatementLog());// <----------
    return handler.<E>query(stmt, resultHandler);// <----------
  } finally {
    closeStatement(stmt);
  }
}

3.9.4 SimpleExecutor#prepareStatement

像jdbc一样,先建立Connection,然后再建Statement

Statement委托给StatementHandler最后去执行。

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;
}

3.10 RoutingStatementHandler#query

public class RoutingStatementHandler implements StatementHandler {
  private final StatementHandler delegate;

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.<E>query(statement, resultHandler);
}

此处delegate的值是:PreparedStatementHandler

3.11 PreparedStatementHandler#query

最后由PreparedStatement执行查询操作,由ResultHandler处理返回结果集。

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  return resultSetHandler.<E> handleResultSets(ps);
}

这就是一个查询的整个调用过程了。

四、MyBatis核心构件

名称 作用
SqlSession 作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
Executor MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合
ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所需要的参数
ResultSetHandler 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换
MappedStatement MappedStatement维护了一条select
SqlSource 负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql 表示动态生成的SQL语句以及相应的参数信息
Configuration MyBatis所有的配置信息都维持在Configuration对象之中

五、事务


MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。

另外需要下载依赖包aopalliance.jar放置到WEB-INF/lib目录下。否则spring初始化时会报异常
java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor

5.1 mybatis-spring的Transaction准备阶段

下面一段是复制别人的博客,感觉有道理,就直接拿来用了。

下面方法中有一个步骤是如果用户没有配置transactionFactory,默认将Mybatis的transactionFactory配置为SpringManagedTransactionFactory,我们在分析Mybatis源码的时候看到,在用户没有指定transactionFactory配置的时候,Mybatis使用ManagedTransactionFactory作为默认的TransactionFactory,Mybatis在创建SqlSession时,需要为其添加一个Executor执行器,构建Executor执行器时需要的Transaction对象就是通过TransactionFactorynewTransaction方法创建的,后续Executor执行sql命令时会通过TransactiongetConnection方法获取数据库连接,这里添加的SpringManagedTransactionFactory有什么作用呢?我们可以思考一下,Spring在开启事务的时候需要获取数据库连接,Mybatis执行的时候也要获取数据库连接,在一次调用过程中,两者配合使用时,如果想让Spring的事务作用于Mybatis的数据库操作,那么在这次调用的过程中两者肯定要共用同一个数据库连接,不然事务无法生效,SpringManagedTransactionFactory就可以解决共用数据库连接的问题。

1.2.4介绍了buildSqlSessionFactory()方法,返回了DefaultSqlSessionFactory

SqlSessionFactoryBean#buildSqlSessionFactory 在创建DefaultSqlSessionFactory时,创建了SpringManagedTransactionFactory,并将它放到configuration中。

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

  Configuration configuration;
  //....
  if (this.transactionFactory == null) {
    this.transactionFactory = new SpringManagedTransactionFactory();//<--
  }
  		
  configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));  

  return this.sqlSessionFactoryBuilder.build(configuration);
}

调用如下图所示:

源码通透-mybatis源码分析以及整合spring过程_第9张图片

SpringManagedTransactionFactory继承了mybatis的TransactionFactory

5.2 mybatis-spring创建SqlSession,并交由spring去得到connection

SqlSession的调用栈如下:

getConnection:150, ConnectionHolder (org.springframework.jdbc.datasource)
doGetConnection:106, DataSourceUtils (org.springframework.jdbc.datasource)
getConnection:77, DataSourceUtils (org.springframework.jdbc.datasource)
openConnection:82, SpringManagedTransaction (org.mybatis.spring.transaction)
getConnection:68, SpringManagedTransaction (org.mybatis.spring.transaction)
getConnection:336, BaseExecutor (org.apache.ibatis.executor)
prepareStatement:84, SimpleExecutor (org.apache.ibatis.executor)
doUpdate:49, SimpleExecutor (org.apache.ibatis.executor)
update:117, BaseExecutor (org.apache.ibatis.executor)
update:76, CachingExecutor (org.apache.ibatis.executor)
update:198, DefaultSqlSession (org.apache.ibatis.session.defaults)
insert:185, DefaultSqlSession (org.apache.ibatis.session.defaults)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
invoke:433, SqlSessionTemplate$SqlSessionInterceptor (org.mybatis.spring)
insert:-1, $Proxy13 (com.sun.proxy)
insert:278, SqlSessionTemplate (org.mybatis.spring)
execute:57, MapperMethod (org.apache.ibatis.binding)
invoke:59, MapperProxy (org.apache.ibatis.binding)
insertStudentByVoMapper:-1, $Proxy25 (com.sun.proxy)
testTransactional:35, StudentService (com.paincupid.springmvc.service)

调用入口已经讲过,SqlSessionInterceptor#invoke阶段,SqlSessionUtils#getSqlSession()方法,再来看一下:

TransactionSynchronizationManager已经是spring包下的类了org.springframework.transaction.support.getResource,根据当前的sessionFactory做key,拿到当前的SqlSessionHolderorg.mybatis.spring.SqlSessionUtils是mybatis-spring包下的类,这个类比较重要,尤其是getSqlSession方法,是spring和mybatis的结合点。

SqlSessionUtils#getSqlSession

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

  notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

  SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

  SqlSession session = sessionHolder(executorType, holder);
  if (session != null) {
    return session;
  }

  session = sessionFactory.openSession(executorType); //new DefaultSqlSession <-----

  registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

  return session;
}

session = sessionFactory.openSession(executorType);

5.2.2 openSession 时创建mybatis事务

本质就是将当前的dataSource放入SpringManagedTransaction对象中

2.3 DefaultSqlSessionFactory#openSession时,会创建事务,前面已介绍过。

DefaultSqlSessionFactory#openSessionFromConnection

final Transaction tx = transactionFactory.newTransaction(connection);

相当于调用SpringManagedTransactionFactory.newTransaction,此处创建的是mybatis事务,SpringManagedTransaction负责mybatis的提交、回滚等。

SpringManagedTransaction后由spring调用:SpringManagedTransaction.commit()(如果事务交由 spring去控制,那么不会真正提交,最后还是调用spring的方法去提交事务。)

为什么spring能调用到SpringManagedTransaction呢? 是因为通过将它注册到spring的事务同步管理器:TransactionSynchronizationManager中,这样就完成了spring对mybatis的事务管理。

public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
  return new SpringManagedTransaction(dataSource);
}
public SpringManagedTransaction(DataSource dataSource) {
  notNull(dataSource, "No DataSource specified");
  this.dataSource = dataSource;
}

5.2.3 保存前当sqlSession,创建并注册事务同步器。

SqlSessionUtils#registerSessionHolder

创建SqlSessionHolder对象, 将当前的session, executorType, exceptionTranslator存进去。然后通过spring的TransactionSynchronizationManager.bindResource方法, sessionFactory做key,holder作value,存起来,下次进getSqlSession时从缓存中取出来直接用,如果能取出holder,并从holer中得到session,则直接返回。

看到TransactionSynchronizationManager.register*()方法,那就是spring和mybatis的结合点,通过mybatis-spring将spring和mybatis结合起来了。

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
  SqlSessionHolder holder;
  if (TransactionSynchronizationManager.isSynchronizationActive()) {
    Environment environment = sessionFactory.getConfiguration().getEnvironment();

    if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
      LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");

      holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
        
      TransactionSynchronizationManager.bindResource(sessionFactory, holder);
      //事务的处理,后面介绍,这里和spring结合,调用spring的TransactionSynchronizationManager
      TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
      holder.setSynchronizedWithTransaction(true);
      holder.requested();
    }//...
  }

TransactionSynchronizationManager.bindResource绑定当前sessionFactoryholder

TransactionSynchronizationManager.registerSynchronization为当前线程注册一个事务同步器,其实就是创建一个SqlSessionSynchronization放到ThreadLocal中,交给spring去管理。后面执行完sql后,提交时,再由spring交由mybatis-spring从TransactionSynchronizationManager取出来,然后从holder中拿 到sqSession提交。

private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
			new NamedThreadLocal<>("Transaction synchronizations");
/**
 * Register a new transaction synchronization for the current thread.
 * Typically called by resource management code.
 * 

Note that synchronizations can implement the * {@link org.springframework.core.Ordered} interface. * They will be executed in an order according to their order value (if any). * @param synchronization the synchronization object to register * @throws IllegalStateException if transaction synchronization is not active * @see org.springframework.core.Ordered */ public static void registerSynchronization(TransactionSynchronization synchronization) throws IllegalStateException { Assert.notNull(synchronization, "TransactionSynchronization must not be null"); if (!isSynchronizationActive()) { throw new IllegalStateException("Transaction synchronization is not active"); } synchronizations.get().add(synchronization); }

5.2.4 SpringManagedTransaction.getConnection()取出当前connection

3.9.4 SimpleExecutor#prepareStatement时,调用getConnect()时,会从之前的SpringManagedTransaction得到当前的connection

protected Connection getConnection(Log statementLog) throws SQLException {
  Connection connection = transaction.getConnection();
  if (statementLog.isDebugEnabled()) {
    return ConnectionLogger.newInstance(connection, statementLog, queryStack);
  } else {
    return connection;
  }
}

SpringManagedTransaction#getConnection

@Override
public Connection getConnection() throws SQLException {
  if (this.connection == null) {
    openConnection();
  }
  return this.connection;
}

SpringManagedTransaction#openConnection

此处是mybatis-spring用来结合spring的,mybatis的dataSource交由spring去管理和获取。也是通过mybatis-spring.jar包中的SpringManagedTransaction

由spring去创建Connection,同时设置this.isConnectionTransactionaltrue,后面调用mybatis的commit方法时,就无法commit,交给spring提交commit。

private void openConnection() throws SQLException {
  this.connection = DataSourceUtils.getConnection(this.dataSource);
  this.autoCommit = this.connection.getAutoCommit();
  this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
}

5.2.5 交由spring根据dataSource去创建Connection

DataSourceUtils#getConnection

org.springframework.jdbc.datasource.DataSourceUtils.getConnection()

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
    try {
        return doGetConnection(dataSource);
    } //...
}

org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection() 如果从事务同步管理器的当前线程中取不到dataSource,则会从jdbc中得到connection,放到ConnectionHolder中。然后:为当前线程注册一个新的事务同步器 和 绑定当前dataSource和holder到事务同步器。

因为本项目用的是druid,所以在IOC启动的时候,dataSource中的connection已经是被druid代理的connection:ConnectionProxyImpl

DataSourceUtils#doGetConnection

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
    Assert.notNull(dataSource, "No DataSource specified");
    // 从当前dataSource中获取绑定的ConnectionHolder
    ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(dataSource);
    if (conHolder == null || !conHolder.hasConnection() && !conHolder.isSynchronizedWithTransaction()) {
        logger.debug("Fetching JDBC Connection from DataSource");
        Connection con = fetchConnection(dataSource);
        //
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            logger.debug("Registering transaction synchronization for JDBC Connection");
            // 在事务中使用相同的连接进一步JDBC操作,线程绑定对象将在事务完成时被同步删除
            ConnectionHolder holderToUse = conHolder;
            if (conHolder == null) {
                holderToUse = new ConnectionHolder(con);
            } else {
                conHolder.setConnection(con);
            }
            // 将ConnectionHolder请求数+1
            holderToUse.requested();           
            // 为当前线程注册一个新的事务同步器
            TransactionSynchronizationManager.registerSynchronization(new DataSourceUtils.ConnectionSynchronization(holderToUse, dataSource));
            holderToUse.setSynchronizedWithTransaction(true);
            if (holderToUse != conHolder) {
                //绑定当前dataSource和holder到事务同步器
                TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
            }
        }

        return con;
    } else {
        conHolder.requested();
        if (!conHolder.hasConnection()) {
            logger.debug("Fetching resumed JDBC Connection from DataSource");
            conHolder.setConnection(fetchConnection(dataSource));
        }

        return conHolder.getConnection();
    }
}

这里我们看到通过调用TransactionSynchronizationManager的getResource方法获取当前线程绑定的ConnectionHolder,TransactionSynchronizationManager这个类我们在分析Spring事务源码的时候看到过,用来管理每个线程的资源和事务同步,内部维护了很多ThreadLocal变量来保存一些线程相关的资源。

方法中我们发现有一个注册事务同步的过程,这个事务同步是做什么用的呢?我们在分析Spring事务源码的时候提到过,在Spring事务回滚、提交、挂起等操作时会激活事务同步的相关方法,而这里添加的事务同步的作用主要是在Spring事务提交、回滚等操作后将ConnectionHolder的请求数量置为0、将一些属性置为初始状态、将数据库连接放回连接池、释放数据库连接等。

===================

5.3 spring事务调用

如果是spring配置了事务,那么会在IOC的时候 ,在@Transactional的注解的Service上,加代理。在调用目标代理方法前,会先调用CGLIB代理:在调用到@Transactional 注解方法时,匹配上了CGLIB代理CglibAopProxy$DynamicAdvisedInterceptor#intercept, 然后会先执行TransactionInterceptor#invoke事务拦截器,通过TransactionAspectSupport#invokeWithinTransaction来调用AbstractPlatformTransactionManager#getTransaction,开始spring事务:getTransaction,在DataSourceTransactionManager#doBegin时,创建链接Connection。

5.3.1 执行过程摘要

invokeWithinTransaction

事务相关的部分,基本上是围绕着 TransactionAspectSupport.invokeWithinTransaction这个方法的,在service方法执行前,先执行createTransactionIfNecessary创建TransactionStatus并保存到当前线程中;第二步执行真正Mapper的sql方法;第三步,在返回前执行事务提交。其本质就是一个环切面。

TransactionAspectSupport#invokeWithinTransaction

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) throws Throwable {

   // If the transaction attribute is null, the method is non-transactional.
   final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
   //创建DataSourceTransactionManager,并为DataSourceTransactionManager赋值dataSoucre属性值 
   final PlatformTransactionManager tm = determineTransactionManager(txAttr);
   final String joinpointIdentification = methodIdentification(method, targetClass);

   if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
      // Standard transaction demarcation with getTransaction and commit/rollback calls.
      // 在执行sql前创建事务,如果加了@Transactional后会走到这里 <----- sql执行前开户事务
      TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
      Object retVal = null;
      try {
         // This is an around advice: Invoke the next interceptor in the chain.
         // This will normally result in a target object being invoked.
         retVal = invocation.proceedWithInvocation(); // <----执行真正mapper方法
      }
      catch (Throwable ex) {
         // target invocation exception
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         cleanupTransactionInfo(txInfo);
      }
      //在执行完service中的mapper sql语句后,会用TransactionStatus执行最后的提交。 
      //<----- sql执行后提交
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }
    //.....
}

5.3.2 执行目标方法(Service.*methoad())前的CGLIB代理拦截器调用

创建PlatformTransactionManager(DataSourceTransactionManager),并为DataSourceTransactionManager的属性dataSoucre赋值

DataSourceTransactionManager继承了AbstractPlatformTransactionManager,创建的其实是DataSourceTransactionManager实例。

源码通透-mybatis源码分析以及整合spring过程_第10张图片

以下栈信息就是创建PlatformTransactionManager:

final PlatformTransactionManager tm = determineTransactionManager(txAttr);

setDataSource:146, DataSourceTransactionManager (org.springframework.jdbc.datasource)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
setValue:344, BeanWrapperImpl$BeanPropertyHandler (org.springframework.beans)
setPropertyValue:454, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValue:280, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValues:95, AbstractPropertyAccessor (org.springframework.beans)
setPropertyValues:75, AbstractPropertyAccessor (org.springframework.beans)
applyPropertyValues:1514, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
populateBean:1226, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:543, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:482, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
getObject:305, AbstractBeanFactory$1 (org.springframework.beans.factory.support)
getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:301, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:201, AbstractBeanFactory (org.springframework.beans.factory.support)
getBeansOfType:534, DefaultListableBeanFactory (org.springframework.beans.factory.support)
getBeansOfType:523, DefaultListableBeanFactory (org.springframework.beans.factory.support)
beansOfTypeIncludingAncestors:261, BeanFactoryUtils (org.springframework.beans.factory)
qualifiedBeanOfType:80, BeanFactoryAnnotationUtils (org.springframework.beans.factory.annotation)
qualifiedBeanOfType:56, BeanFactoryAnnotationUtils (org.springframework.beans.factory.annotation)
determineQualifiedTransactionManager:378, TransactionAspectSupport (org.springframework.transaction.interceptor)
determineTransactionManager:362, TransactionAspectSupport (org.springframework.transaction.interceptor)
invokeWithinTransaction:271, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:96, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:179, ReflectiveMethodInvocation (org.springframework.aop.framework)
intercept:653, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
testTransactional:-1, StudentService$$EnhancerBySpringCGLIB$$4461afed (com.paincupid.springmvc.service)
listTran:50, StudentController (com.paincupid.springmvc.controller)

beanNametransactionManager的类 org.springframework.jdbc.datasource.DataSourceTransactionManagersingleton的。因为DataSourceTransactionManager就单例,所以只有服务重启后,dataSrouce只会被set一次,以后取单例,只会在缓存中取。

CGLIB代理拦截器栈调用过程

doBegin:204, DataSourceTransactionManager (org.springframework.jdbc.datasource)
getTransaction:373, AbstractPlatformTransactionManager (org.springframework.transaction.support)
createTransactionIfNecessary:427, TransactionAspectSupport (org.springframework.transaction.interceptor)
invokeWithinTransaction:276, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:96, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:179, ReflectiveMethodInvocation (org.springframework.aop.framework)
intercept:653, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
testTransactional:-1, StudentService$$EnhancerBySpringCGLIB$$2e9a2dc4 (com.paincupid.springmvc.service)
listTran:28, StudentController (com.paincupid.springmvc.controller)

**摘要过程:AbstractPlatformTransactionManager#getTransaction创建TransactionStatus,返回后绑定到当前线程。 **

DataSourceTransactionManager#doBegin作用

1、 创建Connection 并放到ConnectionHolder中保存起来。

Connection newCon = this.dataSource.getConnection();

txObject.setConnectionHolder(new ConnectionHolder(newCon), true);

2、除了创建connectionHolder,它还会设置并保存上一个事务的隔离级别:isolation level。

Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);

3、最后将dataSrouce和connection绑定到TransactionSynchronizationManager

TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());

getTransaction()方法最主要是功能还是创建TransactionStatus(DefaultTransactionStatus),并返回。

TransactionAspectSupport.createTransactionIfNecessary得到TransactionStatus后,将它包装成TransactionInfo

并把它绑定到当前线程。

TransactionAspectSupport#prepareTransactionInfo

txInfo.bindToThread();

TransactionAspectSupport$TransactionInfo#bindToThread 主要是通过ThreadLocal保存TransactionStatus

private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
			new NamedThreadLocal<TransactionInfo>("Current aspect-driven transaction");
private void bindToThread() {
   // Expose current TransactionStatus, preserving any existing TransactionStatus
   // for restoration after this transaction is complete.
   this.oldTransactionInfo = transactionInfoHolder.get();
   transactionInfoHolder.set(this);
}

执行完代理,会执行真正的service方法:StudentService#testTransactional

5.3.3 执行目标方法(Service.*methoad())

执行方法栈如下图所示。同时可以看到studentVoMapper的执行其它是交给org.apache.ibatis.binding.MapperProxy@7fecf7ab去执行的。上面已经讲解了MapperProxy的调用过程 。

源码通透-mybatis源码分析以及整合spring过程_第11张图片

后执行*mapper.insert方法

5.3.4 执行目标方法(Service.*methoad())后的CGLIB代理拦截器调用:commit提交

commit真正提交栈信息打印

commit:750, DruidPooledConnection (com.alibaba.druid.pool)
doCommit:272, DataSourceTransactionManager (org.springframework.jdbc.datasource)
processCommit:761, AbstractPlatformTransactionManager (org.springframework.transaction.support)
commit:730, AbstractPlatformTransactionManager (org.springframework.transaction.support)
commitTransactionAfterReturning:485, TransactionAspectSupport (org.springframework.transaction.interceptor)
invokeWithinTransaction:291, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:96, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:179, ReflectiveMethodInvocation (org.springframework.aop.framework)
intercept:653, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
testTransactional:-1, StudentService$$EnhancerBySpringCGLIB$$4461afed (com.paincupid.springmvc.service)
listTran:50, StudentController (com.paincupid.springmvc.controller)

/在执行完service中的mapper sql语句后,会用TransactionStatus执行最后的提交。在doBegin的时候,先将自动提交设置成了false.

DataSourceTransactionManager#doBegin 自动提交设置在false,所以在执行真正的service目标方法时不会提交到数据库。

if (con.getAutoCommit()) {
   txObject.setMustRestoreAutoCommit(true);
   if (logger.isDebugEnabled()) {
      logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
   }
   con.setAutoCommit(false);
}

DataSourceTransactionManager#doCommit 真正提交

@Override
protected void doCommit(DefaultTransactionStatus status) {
   DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
   Connection con = txObject.getConnectionHolder().getConnection();
   if (status.isDebug()) {
      logger.debug("Committing JDBC transaction on Connection [" + con + "]");
   }
   try {
      con.commit();
   }
   catch (SQLException ex) {
      throw new TransactionSystemException("Could not commit JDBC transaction", ex);
   }
}

因为项目用的是druid,所以,connection的值为:

com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@28d21ddc

由druid做了connection的代理。

真正提交前的预提交

在真正提交前,会执行预提交。

commit:107, SpringManagedTransaction (org.mybatis.spring.transaction)
commit:244, BaseExecutor (org.apache.ibatis.executor)
commit:119, CachingExecutor (org.apache.ibatis.executor)
commit:224, DefaultSqlSession (org.apache.ibatis.session.defaults)
commit:218, DefaultSqlSession (org.apache.ibatis.session.defaults)
beforeCommit:286, SqlSessionUtils$SqlSessionSynchronization (org.mybatis.spring)
triggerBeforeCommit:95, TransactionSynchronizationUtils (org.springframework.transaction.support)
triggerBeforeCommit:932, AbstractPlatformTransactionManager (org.springframework.transaction.support)
processCommit:744, AbstractPlatformTransactionManager (org.springframework.transaction.support)
commit:730, AbstractPlatformTransactionManager (org.springframework.transaction.support)
commitTransactionAfterReturning:485, TransactionAspectSupport (org.springframework.transaction.interceptor)
invokeWithinTransaction:291, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:96, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:179, ReflectiveMethodInvocation (org.springframework.aop.framework)
intercept:653, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
testTransactional:-1, StudentService$$EnhancerBySpringCGLIB$$4461afed (com.paincupid.springmvc.service)
listTran:50, StudentController (com.paincupid.springmvc.controller)

AbstractPlatformTransactionManager#processCommit

/**
 * Process an actual commit.
 * Rollback-only flags have already been checked and applied.
 * @param status object representing the transaction
 * @throws TransactionException in case of commit failure
 */
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
   try {
      boolean beforeCompletionInvoked = false;
      try {
         prepareForCommit(status);
         triggerBeforeCommit(status); //执行mybatis提交 <-------
         triggerBeforeCompletion(status); //将sessionFactory和connectionHolder从当前线程解绑
         beforeCompletionInvoked = true;
         boolean globalRollbackOnly = false;
         if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
            globalRollbackOnly = status.isGlobalRollbackOnly();
         }
         if (status.hasSavepoint()) {
            //...
            status.releaseHeldSavepoint();
         }
         else if (status.isNewTransaction()) {
            //...
            doCommit(status); //由spring提交 <-------
         }//....
      }//.....

      // Trigger afterCommit callbacks, with an exception thrown there
      // propagated to callers but the transaction still considered as committed.
      try {
         triggerAfterCommit(status);
      }
      finally {
         triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
      }
   }
   finally {
      cleanupAfterCompletion(status);
   }
}

在执行doCommit(status)前,会先执行mybatis的commit: triggerBeforeCommit。会先从当前线程中取出线程同步器TransactionSynchronizationManager.getSynchronizations()

TransactionSynchronizationUtils#triggerBeforeCommit
/**
 * Trigger {@code beforeCommit} callbacks on all currently registered synchronizations.
 * @param readOnly whether the transaction is defined as read-only transaction
 * @throws RuntimeException if thrown by a {@code beforeCommit} callback
 * @see TransactionSynchronization#beforeCommit(boolean)
 */
public static void triggerBeforeCommit(boolean readOnly) {
   for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
      synchronization.beforeCommit(readOnly);
   }
}

此处synchronization就是之前注册过的: SqlSessionSynchronization$SqlSessionSynchronization

在SqlSessionUtils#registerSessionHolder方法中注册的。

TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));

SqlSessionSynchronization$SqlSessionSynchronization#beforeCommit

@Override
public void beforeCommit(boolean readOnly) {
  // Connection commit or rollback will be handled by ConnectionSynchronization or
  // DataSourceTransactionManager.
  // But, do cleanup the SqlSession / Executor, including flushing BATCH statements so
  // they are actually executed.
  // SpringManagedTransaction will no-op the commit over the jdbc connection
  // TODO This updates 2nd level caches but the tx may be rolledback later on! 
  if (TransactionSynchronizationManager.isActualTransactionActive()) {
    try {
      this.holder.getSqlSession().commit(); //<---------
    } //....
  }
}

holder值是SqlSessionHolder

holder = new SqlSessionHolder(session, executorType, exceptionTranslator);

this.holder.getSqlSession()的值为DefaultSqlSession

SqlSessionUtils#getSqlSession

session = sessionFactory.openSession(executorType); //new DefaultSqlSession

DefaultSqlSession.commit --> CachingExecutor.commit --> BaseExecutor.commit --> SpringManagedTransaction.commit

SpringManagedTransaction#commit

@Override
public void commit() throws SQLException {
  if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("Committing JDBC Connection [" + this.connection + "]");
    }
    this.connection.commit();
  }
}

由于 this.isConnectionTransactional值为true,所以不会执行this.connection.commit();,最后交由了spring去提交。

那么 isConnectionTransactional什么时候为 ture 的呢?

SpringManagedTransaction#openConnection()

this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

DataSourceUtils#isConnectionTransactional

public static boolean isConnectionTransactional(Connection con, DataSource dataSource) {
   if (dataSource == null) {
      return false;
   }
   ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
   return (conHolder != null && connectionEquals(conHolder, con));
}

因为之前绑定了dataSource,所以此处ConnectionHolder不为空。connectionEquals为true.

TransactionSynchronizationUtils#triggerBeforeCompletion

TransactionSynchronizationManager.unbindResource(sessionFactory);

将sessionFactory和connectionHolder从当前线程解绑

六 spring事务调用总结


6.1 调用

当前端发起一次请求的时候,调用到目标方法 *Service.*method()方法时,如果加了@Transactional注解,springIOC的启动时,会对当前Service生成代理类,当匹配到@Transactional注解方法时,在执行前和执行后,会执行一些代理方法。事务的执行可以说是围绕着TransactionAspectSupport.invokeWithinTransaction来执行的。

执行前:

  1. 创建DataSourceTransactionManager,并为DataSourceTransactionManager赋值dataSoucre属性值

    final PlatformTransactionManager tm = determineTransactionManager(txAttr);

  2. 前创建事务

    TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

  3. 在创建事务过程中,将dataSourceconnectionHolder注册到事务同步器中,以便为mybatis获取执行,统一dataSource。

    TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());

执行真正的目标方法

可以参考第三章分析的*Mapper调用过程。

这里主要把提交过程中和spring相关的部分提出来。尤其是SqlSessionUtils#getSqlSession方法

  1. 在执行*Mapper的过程中,调用SqlSessionUtils#getSqlSession方法,却获取当前的SqlSession,在执行getSqlSession()方法的过程中:

    A、先偿试从根据key:sessionFactory从TransactionSynchronizationManager中取SqlSessionHolder,获取到的话,直接从SqlSessionHolder中得到SqlSession返回。

    B、如果得不到,会通过sessionFactory.openSession创建SqlSession。在创建过程中,会由SpringManagedTransaction.openConnection会调用spring的类(DataSourceUtils.getConnection)去取connection,从而保证事务是同一个ConnectionDataSourceUtils.doGetConnection会先根据dataSource为key,从

    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource)

    中去取,因为上面已经放进去了,所以从当前线程中就取到了ConnectionHolder,然后返回Connection

    conHolder.getConnection()

执行后

主要是执行commitTransactionAfterReturning(txInfo);方法

  1. TransactionSynchronizationUtils.triggerBeforeCommit预提交。因为之前注册过SqlSessionSynchronization:

    TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));

    所以预提交的时候,从TransactionSynchronizationManager取出SqlSessionSynchronization,然后得到SqlSessionHolder,后执行提交

    this.holder.getSqlSession().commit();

    由于不满足条件,未能真正提交。

  2. 通过``TransactionSynchronizationUtils.triggerBeforeCompletion()sessionFactoryconnectionHolder`从当前线程解绑

    TransactionSynchronizationManager.unbindResource(sessionFactory);

  3. 通过DataSourceTransactionManager.doCommit()真正执行:

    Connection con = txObject.getConnectionHolder().getConnection();

    con.commit();

不同的线程是怎么得到不同的connection?怎么做到同一个事务获取的是同一个connection?

从上面的getSqlSession()过程,每一次请求都会进行一次openSession,去spring中取一次connection,而这个connection可能是交由druid管理,从线程池中取出。

每次请求都会用同一个connection。虽然SessionFactory只会初始化一次,但每次请求前都会将sessionFactory和sessionHolder放到TransactionSynchronizationManager绑定到当前线程,即使一次请求如果有多个Mapper去执行,但放到同一个事物中,那么只会用同一个connection,因为下次还是从当线线程去取。当当线线程执行完所有的操作时,会将当前线程解绑sessionFactory。TransactionSynchronizationManager其实维护的是一个ThreadLocal。那么当下次请求或其它线程再进来时,因为正在执行的线程没有绑定过TransactionSynchronizationManager,所以取不到connectionHolder,这时又会回到线程池中去取connection。

结合 ThreadLocal实现了多么强大的功能!

七、 插一段初始化执行顺序


doCreateBean创建Bean以及Bean的初始化顺序

AbtractAutowireCapableBeanFactory#doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);//<------1.4.1 实例化
   }
   //.....

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      populateBean(beanName, mbd, instanceWrapper); // <--------- 1.4.2 初始化
      if (exposedObject != null) {
         exposedObject = initializeBean(beanName, exposedObject, mbd);
      }
   }
   //....
   return exposedObject;
}

在执行完《1.4.1 实例化》后,会马上执行《1.4.2初始化》吗?

就Mapper的实例化来说,不是的。再通过MapperFactoryBean构造对象创建 MapperFactoryBean(Class mapperInterfacer的属性值为:interface com.paincupid.springmvc.persistence.PersonMapper)后,会行检查它的依赖。发现是SqlSessionFactory,然后再找谁还依赖了SqlSessionFactory,这时候会找到其它的MapperFactoryBean,然后通过反射构造函数将他们new出来,这时候反射时的入参就是其它mapper,比如:interface com.paincupid.springmvc.persistence.StudentMapper,将它放到缓存中。这些都做完后才会执行《1.4.2初始化操作》

*Mapper注入过程,是由service来决定的,当service中有mapper属性性,容器会自动查找符合条件的mapper,找到后进行实例注入。

具体执行步骤:

1、执行行AbstractAutowireCapableBeanFactory#populateBean时,如果RootBeanDefinition的类型是AUTOWIRE_BY_TYPE,那么会执行AbstractAutowireCapableBeanFactory#autowireByType

2、AbstractAutowireCapableBeanFactory#autowireByType 通过resolveDependency 得到sqlSessionFactory实例,并将它保存起来。然后,保存当前beanName和实例的关系: personMapper和sqlSessionFactory。

MutablePropertyValues pvs //是autowireByType的入参
pvs.add(propertyName, autowiredArgument); //autowiredArgument就是sqlSessionFactory实例

3、DefaultListableBeanFactory#resolveDependency --> DefaultListableBeanFactory#doResolveDependency 返回依赖的对象实例sqlSessionFactory

4、DefaultListableBeanFactory#findAutowireCandidates 通过调用beanNamesForTypeIncludingAncestors 返回的名字:sqlSessionFactory,再根据名字去getBean()去得到实例sqlSessionFactory。后返回Map matchingBeans集合

if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {
   result.put(candidateName, getBean(candidateName));
}

5、BeanFactoryUtils#beanNamesForTypeIncludingAncestors --> DefaultListableBeanFactory#getBeanNamesForType --> DefaultListableBeanFactory#doGetBeanNamesForType。根据类型得到beanName。此处类型为org.apache.ibatis.session.SqlSessionFactory,includeNonSingletons=true,allowEagerInit=true。最后返回的是beanName数组,此处数据大小为1,返回的beanName=sqlSessionFactory.

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
   List<String> result = new ArrayList<String>();

   // Check all bean definitions.
   for (String beanName : this.beanDefinitionNames) {
      // Only consider bean as eligible if the bean name
      // is not defined as alias for some other bean.
      if (!isAlias(beanName)) {
         try {
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // Only check bean definition if it is complete.
            if (!mbd.isAbstract() && (allowEagerInit ||
                  ((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
                        !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
               // In case of FactoryBean, match object created by FactoryBean.
               boolean isFactoryBean = isFactoryBean(beanName, mbd);
               boolean matchFound = (allowEagerInit || !isFactoryBean || containsSingleton(beanName)) &&
                     (includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type);
               if (!matchFound && isFactoryBean) {
                  // In case of FactoryBean, try to match FactoryBean instance itself next.
                  beanName = FACTORY_BEAN_PREFIX + beanName; //<---- FactoryBean做特殊处理,加'&'
                  matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type); //此处对于studentMapper组装成有factoryBean来说,仍是false。
               }
               if (matchFound) {//最扣匹配成功的是beanName=sqlSessionFactory
                  result.add(beanName);
               }
            }
         }
         //........
      }
   }

   // Check manually registered singletons too.
   //....	
   return StringUtils.toStringArray(result);
}

6、AbstractBeanFactory#isTypeMatch(String name, ResolvableType typeToMatch)。 name=studentMapper, tryToMatch=org.apache.ibatis.session.SqlSessionFactory。 虽然下面getTypeForFactoryBean只是返回了factoryBean.getObjectType(),但在执行过程中,实例化了FactoryBean(studentMapper)。此处isTypeMatch返回的是false

如果是FactoryBean类型,那么我们想要看的是它创建的类,而不是FactoryBean类本身。

public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
    //......
    // Check bean class whether we're dealing with a FactoryBean.
    if (FactoryBean.class.isAssignableFrom(beanType)) {
        if (!BeanFactoryUtils.isFactoryDereference(name)) {
            // If it's a FactoryBean, we want to look at what it creates, not the factory class.
            beanType = getTypeForFactoryBean(beanName, mbd);
            if (beanType == null) {
                return false;
            }
        }    
    //....   
    return typeToMatch.isAssignableFrom(beanType);    
}

7、AbstractAutowireCapableBeanFactory#getTypeForFactoryBean 在下面调用得到FactoryBean后,返回factoryBean.getObjectType()。getObjectType就FactoryBean的接口方法。

8、AbstractAutowireCapableBeanFactory#getSingletonFactoryBeanForTypeCheck 得到下面调用返回的BeanWrapperImpl,并将它缓存起来。后返回FactoryBean对象。

this.factoryBeanInstanceCache.put(beanName, bw); //bw即BeanWrapperImpl

9、ConstructorResolver#autowireConstructor 通过SimpleInstantiationStrategy#instantiate实例化MapperFactoryBean,并为BeanWrapperImpl的属性赋值,后返回BeanWrapperImpl。autowireConstructor 方法作用:构造函数注入:在这种模式下 ,Spring bean工厂能够宿主基于构造函数的组件依赖性解析。

10、SimpleInstantiationStrategy#instantiate --> BeanUtils#instantiateClass,通过反射构造函数创建MapperFactoryBean对象,mapperInterfacer的属性值为:interface com.paincupid.springmvc.persistence.StudentMapper

下面是实例化MapperFactoryBean(studentMapper)的全过程

:65, MapperFactoryBean (org.mybatis.spring.mapper)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:422, Constructor (java.lang.reflect)
instantiateClass:147, BeanUtils (org.springframework.beans)
instantiate:122, SimpleInstantiationStrategy (org.springframework.beans.factory.support)
autowireConstructor:267, ConstructorResolver (org.springframework.beans.factory.support)
autowireConstructor:1143, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBeanInstance:1046, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
getSingletonFactoryBeanForTypeCheck:865, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
getTypeForFactoryBean:796, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
isTypeMatch:543, AbstractBeanFactory (org.springframework.beans.factory.support)
doGetBeanNamesForType:447, DefaultListableBeanFactory (org.springframework.beans.factory.support)
getBeanNamesForType:423, DefaultListableBeanFactory (org.springframework.beans.factory.support)
beanNamesForTypeIncludingAncestors:220, BeanFactoryUtils (org.springframework.beans.factory)
findAutowireCandidates:1130, DefaultListableBeanFactory (org.springframework.beans.factory.support)
doResolveDependency:1069, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:967, DefaultListableBeanFactory (org.springframework.beans.factory.support)
autowireByType:1292, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
populateBean:1199, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
<--------------
doCreateBean:543, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:482, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
getObject:305, AbstractBeanFactory$1 (org.springframework.beans.factory.support)
getSingleton:230, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:301, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:196, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:753, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:834, AbstractApplicationContext (org.springframework.context.support)

八、其它补充


8.1 Mapper接口对应的bean在容器中的数量

Mapper在容器中是单例的,isSingleton()方法返回的是:true;

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  @Override
  public boolean isSingleton() {
    return true;
  }
}

本文只涉及到部分Mybatis源码解析,以及mybatis-spring怎么把mybatis和spring整合起来,在事务中调用的。

还有很多mybatis的文章,可以参考:Mybatis源码解析优秀博文

你可能感兴趣的:(Spring,spring源码)