Mybatis 初始化是由SqlSessionFactoryBuilder
來完成的戏锹,主要的工作解析XML文件,并將解析的類容封裝到Configuration
類中,最后將Configuration
類封裝到SqlSessionFactory
中并返回,自此初始化完成笋熬。
完成對XML文件解析的是XMLConfigBuilder
、XMLMapperBuilder
腻菇、XMLStatementBuilder
三個類來完成:
- XMLConfigBuilder:負責全局配置文件(mybatis-config.xml)中除了
mappers
節(jié)點的解析胳螟。 - XMLMapperBuilder:負責解析
xxxMapper.xml
映射文件中cache-ref
昔馋、cache
、parameterMap
糖耸、resultMap
秘遏、sql
節(jié)點;根據(jù) namespace 將Mapper接口的動態(tài)代理工廠注冊到 MapperRegistry 中嘉竟。 - XMLStatementBuilder:負責解析
xxxMapper.xml
映射文件中SQL語句節(jié)點邦危,如:select
、insert
舍扰、update
倦蚪、delete
。 - XMLScriptBuilder:負責解析SQL腳本边苹,然后封裝成
SqlSource
陵且。
Mybatis 初始化流程
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder會將解析任務托給XMLConfigBuilder類,源碼如下:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 委托給XMLConfigBuilder去解析配置文件
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 開始解析
return build(parser.parse());
}
...
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
XMLConfigBuilder
負責全局配置文件(mybatis-config.xml)中除了mappers節(jié)點的解析个束。解析<mappers>節(jié)點委托給XMLMapperBuilder解析器慕购。
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 解析<properties>節(jié)點
propertiesElement(root.evalNode("properties"));
// 解析<settings>節(jié)點
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 解析<typeAliases>節(jié)點
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析<mappers>節(jié)點
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// 委托XMLMapperBuilder來解析映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
// 委托XMLMapperBuilder來解析映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
XMLMapperBuilder
負責解析xxxMapper.xml映射文件中cache-ref、cache茬底、parameterMap沪悲、resultMap、sql節(jié)點阱表;根據(jù) namespace 將Mapper接口的動態(tài)代理工廠(MapperProxyFactory)注冊到 MapperRegistry 中殿如。
public void parse() {
// 防止重復解析
if (!configuration.isResourceLoaded(resource)) {
// 解析xxxMapper.xml 映射文件中的 mapper節(jié)點
configurationElement(parser.evalNode("/mapper"));
// 標記為已經(jīng)加載過
configuration.addLoadedResource(resource);
// 根據(jù) namespace 將Mapper接口的動態(tài)代理工廠注冊到 MapperRegistry 中
bindMapperForNamespace();
}
// 兜底
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
// 解析<cache-ref>節(jié)點
cacheRefElement(context.evalNode("cache-ref"));
// 解析<cache>節(jié)點
cacheElement(context.evalNode("cache"));
// 解析<parameterMap>節(jié)點
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 解析<resultMap>節(jié)點
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
// 解析<select|insert|update|delete>節(jié)點
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
// 解析<select|insert|update|delete>節(jié)點委托給 XMLStatementBuilder
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
XMLStatementBuilder
負責解析xxxMapper.xml映射文件中SQL語句節(jié)點,如:select最爬、insert握截、update、delete烂叔。解析<select|insert|update|delete>節(jié)點委托給XMLStatementBuilder解析器。
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// 根據(jù)節(jié)點的名稱來判斷SQL語句的類型(select|insert|update|delete)
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
// 解析<include>節(jié)點
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
// 解析<selectKey>節(jié)點固歪,并在XML中刪除<selectKey>節(jié)點
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
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;
}
// 解析SQL語句蒜鸡,然后封裝成SqlSource
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
// 使用建造者模式構(gòu)建MappedStatement對象
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
Mybatis 初始化流程圖
核心數(shù)據(jù)結(jié)構(gòu)類
Configuration
Configuration
其實就是XML配置文件的Java形態(tài),Configuration是單例的牢裳,生命周期是應用級的逢防;
ResultMap
對應xxxMapper.xml映射文件中的resultMap
節(jié)點。<resultMap>
節(jié)點中的子節(jié)點使用ResultMapping
來封裝蒲讯,如:<id>
忘朝、<result>
等節(jié)點。
MappedStatement
對應xxxMapper.xml映射文件中的<select>
判帮、<insert>
局嘁、<update>
和<delete>
節(jié)點溉箕。
SqlSource
對應xxxMapper.xml映射文件中的sql語句,經(jīng)過解析SqlSource包含的語句最終僅僅包含悦昵?占位符肴茄,可以直接提交給數(shù)據(jù)庫執(zhí)行;
MapperRegistry
它是Mapper接口動態(tài)代理工廠類的注冊中心但指。在MyBatis中寡痰,通過MapperProxy實現(xiàn)InvocationHandler接口,通過MapperProxyFactory生成動態(tài)代理的實例對象棋凳;
MyBatis建造者類圖
Mybatis的整個初始化過程使用了建造者模式拦坠。建造者模式比較適合生成復雜對象,主要關注對象的實例化具體細節(jié)剩岳。