这些构建者都是为mybatis加载资源,解析mybatis-config.xml、*mapper.xml、mapper.class的
按建造者的编写顺序,也是mybatis资源的加载顺序
mybatis解析资源时核心代码调用链
SqlSessionFactoryBuilder#build
(构建SqlSessionFactory)
→XMLConfigBuilder#parseConfiguration
(解析Mybatis-Config.xml)
→XMLConfigBuilder#mapperElement
(解析Mybatis-Config.xml中标签)
→MapperAnnotationBuilder#parse
(解析某一个mapper.class)
→XMLMapperBuilder#parse
(解析某一个mapper.xml+解析挂起的结果集映射+解析挂起的缓存空间引用+解析挂起的Statements)
→XMLMapperBuilder#configurationElement
(解析某一个mapper.xml)
→CacheBuilder#build
(构建二级缓存责任链)
→CacheBuilder#setStandardDecorators
(按需(读配置)设置基准责任链)
→XMLStatementBuilder#parseStatementNode
(解析单个select|insert|update|delete标签)
→XMLScriptBuilder#parseScriptNode
(解析sql语句,非动态sql,将#{},转换成?)
代表
待理清楚
parsePendingResultMaps
resultMap的extends的没有解析,等待其余resultMap都解析完毕再次调用ResultMapResolver的resolve方法解析。此时若从configuration还得不到id为extends的ResultMap则忽略,因为parsePendingResultMaps中捕获了IncompleteElementException并啥也不做。
parsePendingCacheRefs
refId引用的还未解析缓存下来,等到解析完毕再次解析。
parsePendingStatements
Cache-ref未解析完被耽误的MappedStatement再次解析。
SqlSessionFactory
对象//SqlSessionFactoryBuilder#build(InputStream inputStream, String environment, Properties properties)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//1.SqlSessionFactoryBuilder会调用XMLConfigBuilder读取所有的
//MybatisMapConfig.xml和所有的*Mapper.xml文件
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//2.构建Mybatis运行的核心对象Configuration对象 <-(parser.parse())
//3.该Configuration对象作为参数构建一个SqlSessionFactory对象<-build(parser.parse());
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
//该Configuration对象作为参数构建一个SqlSessionFactory对象
return new DefaultSqlSessionFactory(config);
}
Configuration
对象
//org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
//作用:
//1.解析Mybatis-Config.xml
//2.解析配置
public Configuration parse() {
//1.校验是否已经解析过配置
//parsed:是否解析过配置的标识,防止重复解析,浪费性能
//parsed会在XMLConfigBuilder的构建方法中默认复制为false
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//2.解析配置
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//XMLConfigBuilder#parseConfiguration
//解析配置
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//1.解析
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
//2.解析
loadCustomVfs(settings);
loadCustomLogImpl(settings);
//3.解析
typeAliasesElement(root.evalNode("typeAliases"));
//4.解析
pluginElement(root.evalNode("plugins"));
//5.解析
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 标签赋值
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//6.解析
environmentsElement(root.evalNode("environments"));
//7.解析 数据库厂商标识,支持是基于映射语句中的 databaseId 属性
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//8.解析 类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
//9.解析映射器
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) {
/*parent:
*/
for (XNode child : parent.getChildren()) {
//1.单个单个的解析
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 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 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.");
}
}
}
}
}
XMLConfigBuilder在构建Configuration对象时,也会调用XMLMapperBuilder用于读取*Mapper
文件
路径:XMLConfigBuilder#mapperElement→Configuration#addMapper→MapperRegistry#addMapper
→MapperAnnotationBuilder#parse
//org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse
public void parse() {
//type是Mapper.class
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
//解析mapper.xml,调用XMLMapperBuilder!!!!!!!
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
//解析注解@CacheNamespace
parseCache();
//解析注解@CacheNamespaceRef
parseCacheRef();
//解析Mapper.class的所有方法
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
//解析CacheNamespaceRef
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
路径:MapperAnnotationBuilder#parse→MapperAnnotationBuilder#loadXmlResource
→XMLMapperBuilder#parse
public void parse() {
//resource:即mapper.xml
if (!configuration.isResourceLoaded(resource)) {//避免重复解析
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);//存入Set,表明已经解析过了
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
//XMLMapperBuilder#configurationElement
//context 某个mapper.xml文件
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);
cacheRefElement(context.evalNode("cache-ref"));
//1.解析 标签,调用***CacheBuilder***
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
//2.解析一堆
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);
}
}
XMLMapperBuilder会使用XMLStatementBuilder来读取和build所有的SQL语句
XMLMapperBuilder会使用CacheBuilder来读取和build所有的Cache
路径-注解:MapperAnnotationBuilder#parse→MapperAnnotationBuilder→parseCache
→MapperBuilderAssistant#useNewCache→CacheBuilder#build
路径-xml:XMLMapperBuilder#configurationElement→XMLMapperBuilder#cacheElement
→MapperBuilderAssistant#useNewCache→CacheBuilder#build
public Cache build() {
//1.声明默认(未配置的情况下)的缓存责任链,PerpetualCache和LruCache
setDefaultImplementations();
//生成Cache,未配置默认情况是生成PerpetualCache
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
// issue #352, do not apply decorators to custom caches
if (PerpetualCache.class.equals(cache.getClass())) {
//默认情况下是LruCache
for (Class<? extends Cache> decorator : decorators) {
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
//默认情况下,生成基准缓存责任链
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
cache = new LoggingCache(cache);
}
return cache;
}
//按需生成基准缓存责任链
private Cache setStandardDecorators(Cache cache) {
try {
MetaObject metaCache = SystemMetaObject.forObject(cache);
if (size != null && metaCache.hasSetter("size")) {
metaCache.setValue("size", size);
}
//clearInterval 配置中读取
if (clearInterval != null) {
cache = new ScheduledCache(cache);
((ScheduledCache) cache).setClearInterval(clearInterval);
}
//配置中读取,默认true(应该是)
if (readWrite) {
cache = new SerializedCache(cache);
}
cache = new LoggingCache(cache);
cache = new SynchronizedCache(cache);
//配置中读取
if (blocking) {
cache = new BlockingCache(cache);
}
return cache;
} catch (Exception e) {
throw new CacheException("Error building standard cache decorators. Cause: " + e, e);
}
}
标签路径:XMLMapperBuilder#buildStatementFromContext→XMLStatementBuilder#parseStatementNode
//解析单个
//1.解析标签内的sql语句,调用XMLScriptBuilder#parseScriptNode
//2.其他都是解析标签中的属性值
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
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());
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.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: and 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;
}
//1.此处解析标签内的sql语句,调用XMLScriptBuilder#parseScriptNode
//2.其他都是解析标签中的属性值
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);
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
路径:XMLStatementBuilder#parseStatementNode→LanguageDriver#createSqlSource
→XMLLanguageDriver#createSqlSource→XMLScriptBuilder#parseScriptNode
public SqlSource parseScriptNode() {
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource;
if (isDynamic) {
//0.没啥处理的,应该是动态sql执行的时候才解析
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
//1.将sql中的#{},转换成?
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
Mybatis是使用这些构建者来做环境初始化的
所有的建造者设计都是为了复杂对象,客户构建简单
建造者核心步骤调用链(省略的路径看上面写的对应的核心方法)
SqlSessionFactoryBuilder#build
(构建SqlSessionFactory)
→XMLConfigBuilder#parseConfiguration
(解析Mybatis-Config.xml)
→XMLConfigBuilder#mapperElement
(解析Mybatis-Config.xml中标签)
→MapperAnnotationBuilder#parse
(解析某一个mapper.class)
→XMLMapperBuilder#parse
(解析某一个mapper.xml+解析挂起的结果集映射+解析挂起的缓存空间引用+解析挂起的Statements)
→XMLMapperBuilder#configurationElement
(解析某一个mapper.xml)
→CacheBuilder#build
(构建二级缓存责任链)
→CacheBuilder#setStandardDecorators
(按需(读配置)设置基准责任链)
→XMLStatementBuilder#parseStatementNode
(解析单个select|insert|update|delete标签)
→XMLScriptBuilder#parseScriptNode
(解析sql语句,非动态sql,将#{},转换成?)
这些构建者的核心方法
build(Configuration config)
parseConfiguration
mapperElement
parse
parse
configurationElement
build
setStandardDecorators
parseStatementNode
parseScriptNode
几个构造者的包路径