MyBatis 源码分析--SqlSessionFactory

前言:

前文我们简单的回顾了 MyBatis 的基本概念,有聊到核心组件,工作流程等,本篇我们开始深入剖析 MyBatis 的核心源码,欢迎大家持续关注。

Mybatis 知识传送门

初识 MyBatis 【MyBatis 核心概念】

MyBatis 源码解析

为了方便我们分析 MyBatis 源码,我们先写一段简单的小程序,来辅助我们分析 MyBatis 源码,如下:

public class MyBatisTest {
    @Test
    public void test() throws IOException {
        //读取配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        //创建 SqlSessionFactoryBuilder 对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //通过 SqlSessionBuilder 对象 解析 mybatis-config.xml 文件 构建一个SqlSessionFactory 
        SqlSessionFactory sqlSessionFactory = builder.build(is);
        //通过SqlSessionFactory构建一个SqlSession
        SqlSession session = sqlSessionFactory.openSession();
        //通过SqlSession 获取 Mapper 实例
        UserMapper userMapper = session.getMapper(UserMapper.class);
        //获取数据
		List<User> users = userMapper.findAll();
        //打印输出
        for (User user : users) {
            System.out.println(user);
        }
        //关闭资源
        session.close();
        is.close();
    }
}

在分析创建 SqlSessionFactory 源码之前,先分享一个代码流程简图,帮助梳理源码脉络。

在这里插入图片描述

获取 SqlSessionFactory 源码分析

SqlSessionFactory 工厂是通过 SqlSessionFactoryBuilder#build 方法获取的,build 方法会先创建一个 XMLConfigBuilder ,XMLConfigBuilder 会去解析 XML 文件,生成一个 SqlSessionFactory 方返回,我们重点关注 var5 = this.build(parser.parse()) 这行代码。

//org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.Reader, java.lang.String, java.util.Properties)
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
	//SqlSessionFactory
	SqlSessionFactory var5;
	try {
		//根据入参创建 xml 配置生成器   
		XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
		//parser.parse() :解析 xml 文件  重点关注
		//创建 SqlSessionFactory 并返回
		var5 = this.build(parser.parse());
	} catch (Exception var14) {
		throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
	} finally {
		ErrorContext.instance().reset();

		try {
			reader.close();
		} catch (IOException var13) {
		}

	}

	return var5;
}

XMLConfigBuilder#parse 方法源码分析

XMLConfigBuilder#parse 方法会先判断当前 XML 文件是否解析过,如果解析过就抛出异常,没有解析过就正常开始解析文件 。

//org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
public Configuration parse() {
	//判断是否被解析过
	if (this.parsed) {
		//解析过再次被解析就要抛出异常了
		throw new BuilderException("Each XMLConfigBuilder can only be used once.");
	} else {
		//标记为已经解析
		this.parsed = true;
		//解析配置 xml 文件  重点关注
		this.parseConfiguration(this.parser.evalNode("/configuration"));
		//返回 Configuration
		return this.configuration;
	}
}

XMLConfigBuilder#parseConfiguration 方法源码分析

XMLConfigBuilder#parseConfiguration 方法会解析 XML 文件的各种属性,最后会去加载 Mapper 文件,也就是我们写的 SQL 文件。

//org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
	try {
		//解析 properties 节点
		this.propertiesElement(root.evalNode("properties"));
		//解析 Setting 节点
		Properties settings = this.settingsAsProperties(root.evalNode("settings"));
		//加载自定义的 VFS  VFS 主要用来加载容器内的各种资源 例如 jar 或者 class文件
		this.loadCustomVfs(settings);
		//加载自定义的日志实现
		this.loadCustomLogImpl(settings);
		//解析 typeAliases 
		this.typeAliasesElement(root.evalNode("typeAliases"));
		//加载 插件 
		this.pluginElement(root.evalNode("plugins"));
		//加载对象工厂
		this.objectFactoryElement(root.evalNode("objectFactory"));
		//加载对象包装工厂
		this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
		//加载反射工厂
		this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
		//把 settings 赋值给 confiuration
		this.settingsElement(settings);
		//加载环境配置
		this.environmentsElement(root.evalNode("environments"));
		//加载数据库标识
		this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
		//加载 typeHandler
		this.typeHandlerElement(root.evalNode("typeHandlers"));
		//加载 mapper 文件 也就是我们写的 sql 文件  重点关注
		this.mapperElement(root.evalNode("mappers"));
	} catch (Exception var3) {
		throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
	}
}

XMLConfigBuilder#mapperElement 方法源码分析

XMLConfigBuilder#mapperElement 方法会解析 SQL 文件的配置方式,在 Mappers 下 Mapper 文件有四种配置方式,分别是 package、resource、url、class,mapperElement 方法对其进行了区分处理,我们重点关注 mapperParser.parse() 方法。

//org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement
private void mapperElement(XNode parent) throws Exception {
	if (parent != null) {
		//获取 mappers 的子节点 也就是 mapper 节点 迭代遍历
		Iterator var2 = parent.getChildren().iterator();

		while(true) {
			while(var2.hasNext()) {
				//Mappers 的子节点  只能是 package 节点 和 mapper 节点
				XNode child = (XNode)var2.next();
				String resource;
				//是否是 packge 节点
				if ("package".equals(child.getName())) {
					//获取需要扫描的包路径 
					resource = child.getStringAttribute("name");
					//加入到 configuration 中
					this.configuration.addMappers(resource);
				} else {
					//获取 resource、url、class这三个属性  只能有一个生效
					//获取 resource 
					resource = child.getStringAttribute("resource");
					//获取 url  
					String url = child.getStringAttribute("url");
					//获取 mapperClass
					String mapperClass = child.getStringAttribute("class");
					//xml 解析器
					XMLMapperBuilder mapperParser;
					InputStream inputStream;
					if (resource != null && url == null && mapperClass == null) {
						//解析 resource 类路径
						ErrorContext.instance().resource(resource);
						inputStream = Resources.getResourceAsStream(resource);
						mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
						mapperParser.parse();
					} else if (resource == null && url != null && mapperClass == null) {
						//解析 url 绝对url路径
						ErrorContext.instance().resource(url);
						inputStream = Resources.getUrlAsStream(url);
						mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
						mapperParser.parse();
					} else {
						if (resource != null || url != null || mapperClass == null) {
							throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
						}
						//解析 mapperclass  类名
						Class<?> mapperInterface = Resources.classForName(mapperClass);
						//加入到 configuration 中
						this.configuration.addMapper(mapperInterface);
					}
				}
			}

			return;
		}
	}
}

Configuration#addMappers 方法源码分析

addMappers 方法会把包下面的所有 Mapper 解析出来,加入到一个 knownMappers 的 HashMap中。

//org.apache.ibatis.session.Configuration#addMappers
public void addMappers(String packageName) {
	this.mapperRegistry.addMappers(packageName);
}


//org.apache.ibatis.binding.MapperRegistry#addMappers(java.lang.String)
public void addMappers(String packageName) {
	this.addMappers(packageName, Object.class);
}


//org.apache.ibatis.binding.MapperRegistry#addMappers
public void addMappers(String packageName, Class<?> superType) {
	//处理 package 下的 所有 mapper 
	ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
	resolverUtil.find(new IsA(superType), packageName);
	Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
	Iterator var5 = mapperSet.iterator();
	//循环遍历每个 Mapper
	while(var5.hasNext()) {
		Class<?> mapperClass = (Class)var5.next();
		//加入到 knownMappers Map 中
		this.addMapper(mapperClass);
	}

}


//org.apache.ibatis.binding.MapperRegistry#addMapper
public <T> void addMapper(Class<T> type) {
	if (type.isInterface()) {
		//判断 Mapper 是否存在
		if (this.hasMapper(type)) {
			throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
		}

		boolean loadCompleted = false;

		try {
			//将 Mapper 的 Class 做为 key value 为 代理对象工厂
			this.knownMappers.put(type, new MapperProxyFactory(type));
			//解析注解方式的 Mapper
			MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
			parser.parse();
			loadCompleted = true;
		} finally {
			if (!loadCompleted) {
				//失败 从 Mapper 中移出
				this.knownMappers.remove(type);
			}

		}
	}

}

XMLMapperBuilder#parse 方法源码分析

XMLMapperBuilder#parse 方法主要作用就是解析 Mapper 文件,加载 Resouce,创建 MapperProxyFactory,我们重点分析解析 Mapper 标签部分。

//org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
public void parse() {
	//是否加载过
	if (!this.configuration.isResourceLoaded(this.resource)) {
		//解析 mapper 标签
		this.configurationElement(this.parser.evalNode("/mapper"));
		//加载 resource
		this.configuration.addLoadedResource(this.resource);
		//为名称空间绑定映射器 其实就是创建 MapperProxyFactory
		this.bindMapperForNamespace();
	}
	//结果集
	this.parsePendingResultMaps();
	//缓存
	this.parsePendingCacheRefs();
	//待处理的sql
	this.parsePendingStatements();
}

XMLMapperBuilder#configurationElement 方法源码分析

XMLMapperBuilder#configurationElement 方法已经开始解析 Mapper 文件了,会对 Mapper 文件中的一些标签进行解析,并去构建 SQL 语句,我们重点关注 this.buildStatementFromContext(context.evalNodes(“select|insert|update|delete”))。

//org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement
private void configurationElement(XNode context) {
	try {
		//获取 namespace
		String namespace = context.getStringAttribute("namespace");
		if (namespace != null && !namespace.isEmpty()) {
			//设置 namespace
			this.builderAssistant.setCurrentNamespace(namespace);
			//解析 cache-ref 标签
			this.cacheRefElement(context.evalNode("cache-ref"));
			//解析 cache 标签
			this.cacheElement(context.evalNode("cache"));
			//解析映射参数 parameterMap 
			this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
			//解析结果集 resultMap
			this.resultMapElements(context.evalNodes("/mapper/resultMap"));
			//解析 sql
			this.sqlElement(context.evalNodes("/mapper/sql"));
			//构建 crud 语句 重点关注
			this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
		} else {
			throw new BuilderException("Mapper's namespace cannot be empty");
		}
	} catch (Exception var3) {
		throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
	}
}

XMLMapperBuilder#buildStatementFromContext 方法源码分析

XMLMapperBuilder#buildStatementFromContext 方法包装调用了 XMLMapperBuilder#buildStatementFromContext 方法,主要做的事情就是迭代解析构建 SQL 语句。

//org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)
private void buildStatementFromContext(List<XNode> list) {
	if (this.configuration.getDatabaseId() != null) {
		//database 不为空 构建 sql 语句
		this.buildStatementFromContext(list, this.configuration.getDatabaseId());
	}
	//构建 sql 语句
	this.buildStatementFromContext(list, (String)null);
}


//org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>, java.lang.String)
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
	//迭代遍历 sql 语句
	Iterator var3 = list.iterator();

	while(var3.hasNext()) {
		XNode context = (XNode)var3.next();
		//创建 XMLStatementBuilder 解析器
		XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);

		try {
			//解析 sql  重点关注
			statementParser.parseStatementNode();
		} catch (IncompleteElementException var7) {
			this.configuration.addIncompleteStatement(statementParser);
		}
	}

}

XMLStatementBuilder#parseStatementNode 方法源码分析

XMLStatementBuilder#parseStatementNode 方法创建了 SqlSource ,并获取一系列的属性,最后封装成了 MappedStatement 对象。

//org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode
public void parseStatementNode() {
	//获取 id 属性
	String id = this.context.getStringAttribute("id");
	//获取 database id
	String databaseId = this.context.getStringAttribute("databaseId");
	//启用的数据库和sql节点配置的是否匹配
	if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
		//匹配
		//获取 nodeName
		String nodeName = this.context.getNode().getNodeName();
		//获取 sql 命令类型
		SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
		//是否是 select 类型
		boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
		//是否需要刷新本地缓存 和 二级缓存
		boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
		//是否需要使用二级缓存
		boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
		//这个设置仅针对嵌套结果 select 语句 如果为 true 将会假设包含了嵌套结果集或是分组 当返回一个主结果行时 就不会产生对前面结果集的引用 这就使得在获取嵌套结果集的时候不至于内存不够用 默认值:false。
		boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
		//xml 转换其
		XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
		//适用到 node
		includeParser.applyIncludes(this.context.getNode());
		//获取参数类型
		String parameterType = this.context.getStringAttribute("parameterType");
		//解析参数类型的 class
		Class<?> parameterTypeClass = this.resolveClass(parameterType);
		//获取 long 属性
		String lang = this.context.getStringAttribute("lang");
		//动态sql 语言驱动器
		LanguageDriver langDriver = this.getLanguageDriver(lang);
		//处理  selectKey节点  当数据表中主键设计为自增 可能会存在业务需要在插入后获取到主键 这时候就需要使用 selectKey 节点 解析完毕后删除 selectKey 节点
		this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
		//拼接id
		String keyStatementId = id + "!selectKey";
		//名称空间+.+keyStatementId
		keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
		//获取 keyGenerator
		Object keyGenerator;
		if (this.configuration.hasKeyGenerator(keyStatementId)) {
			keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
		} else {
			keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
		}
		//创建  SqlSource 这里面有是否动态 sql 的判断
		SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
		//获取 StatementType  STATEMENT:普通语句    PREPARED:预处理     CALLABLE:存储过程
		StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
		//获取 fetchSize
		Integer fetchSize = this.context.getIntAttribute("fetchSize");
		//获取 timeout
		Integer timeout = this.context.getIntAttribute("timeout");
		//获取入参 parameterMap
		String parameterMap = this.context.getStringAttribute("parameterMap");
		//获取 resultType
		String resultType = this.context.getStringAttribute("resultType");
		//获取 resultType 的类型
		Class<?> resultTypeClass = this.resolveClass(resultType);
		//获取 resultMap
		String resultMap = this.context.getStringAttribute("resultMap");
		//获取 resultSetType
		String resultSetType = this.context.getStringAttribute("resultSetType");
		//获取 ResultSetType
		ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
		if (resultSetTypeEnum == null) {
			//获取默认的 ResultSetType
			resultSetTypeEnum = this.configuration.getDefaultResultSetType();
		}
		//获取 keyProperty
		String keyProperty = this.context.getStringAttribute("keyProperty");
		//获取 keyColumn
		String keyColumn = this.context.getStringAttribute("keyColumn");
		//获取 resultSets
		String resultSets = this.context.getStringAttribute("resultSets");
		//创建 SQL 对应的 MappedStatement 对象
		this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
	}
}

XMLLanguageDriver#createSqlSource 方法源码分析

XMLLanguageDriver#createSqlSource 方法中有对动态 SQL 的处理 ,对于动态 SQL 和普通 SQL 会有不同的处理。

  • RawSqlSource : 存储的是只有 #{} 或者没有标签的纯文本SQL信息。
  • DynamicSqlSource : 存储的是写有 ${} 或者具有动态SQL标签的SQL信息。
  • StaticSqlSource : 是DynamicSqlSource和RawSqlSource解析为BoundSql的一个中间态对象类型。
  • BoundSql:用于生成我们最终执行的SQL语句,属性包括参数值、映射关系、以及SQL(带问号的)。
//org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class<?>)
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
	//创建 XMLScriptBuilder 解析 SQL
	XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
	return builder.parseScriptNode();
}


//org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode
public SqlSource parseScriptNode() {
	//解析动态标签 
	MixedSqlNode rootSqlNode = this.parseDynamicTags(this.context);
	Object sqlSource;
	//是否是动态 sql
	if (this.isDynamic) {
		//创建动态 SQL DynamicSqlSource 
		sqlSource = new DynamicSqlSource(this.configuration, rootSqlNode);
	} else {
		//普通 SQL RawSqlSource
		sqlSource = new RawSqlSource(this.configuration, rootSqlNode, this.parameterType);
	}

	return (SqlSource)sqlSource;
}


//org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseDynamicTags
protected MixedSqlNode parseDynamicTags(XNode node) {
	List<SqlNode> contents = new ArrayList();
	NodeList children = node.getNode().getChildNodes();

	for(int i = 0; i < children.getLength(); ++i) {
		XNode child = node.newXNode(children.item(i));
		String nodeName;
		if (child.getNode().getNodeType() != 4 && child.getNode().getNodeType() != 3) {
			if (child.getNode().getNodeType() == 1) {
				nodeName = child.getNode().getNodeName();
				XMLScriptBuilder.NodeHandler handler = (XMLScriptBuilder.NodeHandler)this.nodeHandlerMap.get(nodeName);
				if (handler == null) {
					throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
				}

				handler.handleNode(child, contents);
				this.isDynamic = true;
			}
		} else {
			nodeName = child.getStringBody("");
			//重点关注这里 将节点封装成 TextSqlNode
			TextSqlNode textSqlNode = new TextSqlNode(nodeName);
			// textSqlNode.isDynamic() 判断是否是动态的
			if (textSqlNode.isDynamic()) {
				contents.add(textSqlNode);
				//设置为 是
				this.isDynamic = true;
			} else {
				contents.add(new StaticTextSqlNode(nodeName));
			}
		}
	}

	return new MixedSqlNode(contents);
}


//org.apache.ibatis.scripting.xmltags.TextSqlNode#isDynamic
public boolean isDynamic() {
	//创建一个 DynamicCheckerTokenParser
	TextSqlNode.DynamicCheckerTokenParser checker = new TextSqlNode.DynamicCheckerTokenParser();
	//创建一个以 ${ 为开始和以 } 为结尾的解析器 DynamicCheckerTokenParser 
	GenericTokenParser parser = this.createParser(checker);
	//解析
	parser.parse(this.text);
	//解析成功 返回 
	return checker.isDynamic();
}

//org.apache.ibatis.scripting.xmltags.TextSqlNode#createParser
private GenericTokenParser createParser(TokenHandler handler) {
	return new GenericTokenParser("${", "}", handler);
}

createSqlSource 源码分析完毕,我们继续分析创建 SQL 对应的 MappedStatement 对象的源码。

MapperBuilderAssistant#addMappedStatement 方法源码分析

MapperBuilderAssistant#addMappedStatement 方法的主要作用就是将 MappedStatement 加入 mappedStatements 集合中。

//org.apache.ibatis.builder.MapperBuilderAssistant#addMappedStatement
public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets) {
	if (this.unresolvedCacheRef) {
		throw new IncompleteElementException("Cache-ref not yet resolved");
	} else {
		//获取 名称空间
		id = this.applyCurrentNamespace(id, false);
		//是否是 select 类型
		boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
		//创建 statementBuilder
		org.apache.ibatis.mapping.MappedStatement.Builder statementBuilder = (new org.apache.ibatis.mapping.MappedStatement.Builder(this.configuration, id, sqlSource, sqlCommandType)).resource(this.resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(this.getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired((Boolean)this.valueOrDefault(flushCache, !isSelect)).useCache((Boolean)this.valueOrDefault(useCache, isSelect)).cache(this.currentCache);
		ParameterMap statementParameterMap = this.getStatementParameterMap(parameterMap, parameterType, id);
		if (statementParameterMap != null) {
			//给 mappedStatement 赋值 parameterMap
			statementBuilder.parameterMap(statementParameterMap);
		}

		MappedStatement statement = statementBuilder.build();
		//将 MappedStatement 加入 mappedStatements 集合中
		this.configuration.addMappedStatement(statement);
		return statement;
	}
}


//org.apache.ibatis.session.Configuration#addMappedStatement
public void addMappedStatement(MappedStatement ms) {
	//将 MappedStatement 加入 mappedStatements 集合中
	this.mappedStatements.put(ms.getId(), ms);
}

this.build(parser.parse()) 源码分析

经过以上步骤,Configuration 已经加载完毕,调用SqlSessionFactoryBuilder#build 方法创建 SqlSessionFactory 。

//org.apache.ibatis.session.SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)
public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
 }

至此 SqlSessionFactory 创建的源码分析完毕,希望可以帮助到有需要的小伙伴。

欢迎提出建议及对错误的地方指出纠正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值