public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//mybatis解析的主流程邏輯在這里開(kāi)始
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
- 可以看到首先判斷了這個(gè)配置文件有沒(méi)有解析唉侄,只能允許沒(méi)有解析過(guò)的進(jìn)行解析税手。其中調(diào)用了parser.evalNode(“/configuration”)返回根節(jié)點(diǎn)的org.apache.ibatis.parsing.XNode表示,XNode里面主要把關(guān)鍵的節(jié)點(diǎn)屬性和占位符變量結(jié)構(gòu)化出來(lái)箩艺,后面我們?cè)倏戳剐埂H缓笳{(diào)用parseConfiguration根據(jù)mybatis的主要配置進(jìn)行解析,如下所示:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
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"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
所有的root.evalNode底層都是調(diào)用XML DOM的evaluate()方法猾骡,根據(jù)給定的節(jié)點(diǎn)表達(dá)式來(lái)計(jì)算指定的 XPath 表達(dá)式,并且返回一個(gè)XPathResult對(duì)象敷搪,返回類型在Node.evalNode()方法中均被指定為NODE。
在詳細(xì)解釋每個(gè)節(jié)點(diǎn)的解析前幢哨,我們先來(lái)粗略看下mybatis-3-config.dtd的聲明:
<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
<!ELEMENT databaseIdProvider (property*)>
<!ATTLIST databaseIdProvider
type CDATA #REQUIRED
>
<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>
<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT settings (setting+)>
<!ELEMENT setting EMPTY>
<!ATTLIST setting
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT typeAliases (typeAlias*,package*)>
<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
type CDATA #REQUIRED
alias CDATA #IMPLIED
>
<!ELEMENT typeHandlers (typeHandler*,package*)>
<!ELEMENT typeHandler EMPTY>
<!ATTLIST typeHandler
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
handler CDATA #REQUIRED
>
<!ELEMENT objectFactory (property*)>
<!ATTLIST objectFactory
type CDATA #REQUIRED
>
<!ELEMENT objectWrapperFactory EMPTY>
<!ATTLIST objectWrapperFactory
type CDATA #REQUIRED
>
<!ELEMENT reflectorFactory EMPTY>
<!ATTLIST reflectorFactory
type CDATA #REQUIRED
>
<!ELEMENT plugins (plugin+)>
<!ELEMENT plugin (property*)>
<!ATTLIST plugin
interceptor CDATA #REQUIRED
>
<!ELEMENT environments (environment+)>
<!ATTLIST environments
default CDATA #REQUIRED
>
<!ELEMENT environment (transactionManager,dataSource)>
<!ATTLIST environment
id CDATA #REQUIRED
>
<!ELEMENT transactionManager (property*)>
<!ATTLIST transactionManager
type CDATA #REQUIRED
>
<!ELEMENT dataSource (property*)>
<!ATTLIST dataSource
type CDATA #REQUIRED
>
<!ELEMENT mappers (mapper*,package*)>
<!ELEMENT mapper EMPTY>
<!ATTLIST mapper
resource CDATA #IMPLIED
url CDATA #IMPLIED
class CDATA #IMPLIED
>
<!ELEMENT package EMPTY>
<!ATTLIST package
name CDATA #REQUIRED
>
從上述DTD可知赡勘,mybatis-config文件最多有11個(gè)配置項(xiàng),分別是properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?捞镰,但是所有的配置都是可選的闸与,這意味著mybatis-config配置文件本身可以什么都不包含。因?yàn)樗械呐渲米詈蟊4娴給rg.apache.ibatis.session.Configuration中岸售,所以在詳細(xì)查看每塊配置的解析前践樱,我們來(lái)看下Configuration的內(nèi)部完整結(jié)構(gòu):
public class Configuration {
protected Environment environment;
// 允許在嵌套語(yǔ)句中使用分頁(yè)(RowBounds)。如果允許使用則設(shè)置為false凸丸。默認(rèn)為false
protected boolean safeRowBoundsEnabled;
// 允許在嵌套語(yǔ)句中使用分頁(yè)(ResultHandler)拷邢。如果允許使用則設(shè)置為false。
protected boolean safeResultHandlerEnabled = true;
// 是否開(kāi)啟自動(dòng)駝峰命名規(guī)則(camel case)映射屎慢,即從經(jīng)典數(shù)據(jù)庫(kù)列名 A_COLUMN 到經(jīng)典 Java 屬性名 aColumn 的類似映射瞭稼。默認(rèn)false
protected boolean mapUnderscoreToCamelCase;
// 當(dāng)開(kāi)啟時(shí)忽洛,任何方法的調(diào)用都會(huì)加載該對(duì)象的所有屬性。否則环肘,每個(gè)屬性會(huì)按需加載欲虚。默認(rèn)值false (true in ≤3.4.1)
protected boolean aggressiveLazyLoading;
// 是否允許單一語(yǔ)句返回多結(jié)果集(需要兼容驅(qū)動(dòng))。
protected boolean multipleResultSetsEnabled = true;
// 允許 JDBC 支持自動(dòng)生成主鍵悔雹,需要驅(qū)動(dòng)兼容复哆。這就是insert時(shí)獲取mysql自增主鍵/oracle sequence的開(kāi)關(guān)。注:一般來(lái)說(shuō),這是希望的結(jié)果,應(yīng)該默認(rèn)值為true比較合適腌零。
protected boolean useGeneratedKeys;
// 使用列標(biāo)簽代替列名,一般來(lái)說(shuō),這是希望的結(jié)果
protected boolean useColumnLabel = true;
// 是否啟用緩存
protected boolean cacheEnabled = true;
// 指定當(dāng)結(jié)果集中值為 null 的時(shí)候是否調(diào)用映射對(duì)象的 setter(map 對(duì)象時(shí)為 put)方法寂恬,這對(duì)于有 Map.keySet() 依賴或 null 值初始化的時(shí)候是有用的。
protected boolean callSettersOnNulls;
// 允許使用方法簽名中的名稱作為語(yǔ)句參數(shù)名稱莱没。 為了使用該特性初肉,你的工程必須采用Java 8編譯,并且加上-parameters選項(xiàng)饰躲。(從3.4.1開(kāi)始)
protected boolean useActualParamName = true;
//當(dāng)返回行的所有列都是空時(shí)牙咏,MyBatis默認(rèn)返回null。 當(dāng)開(kāi)啟這個(gè)設(shè)置時(shí)嘹裂,MyBatis會(huì)返回一個(gè)空實(shí)例妄壶。 請(qǐng)注意,它也適用于嵌套的結(jié)果集 (i.e. collectioin and association)寄狼。(從3.4.2開(kāi)始) 注:這里應(yīng)該拆分為兩個(gè)參數(shù)比較合適, 一個(gè)用于結(jié)果集丁寄,一個(gè)用于單記錄。通常來(lái)說(shuō)泊愧,我們會(huì)希望結(jié)果集不是null伊磺,單記錄仍然是null
protected boolean returnInstanceForEmptyRow;
// 指定 MyBatis 增加到日志名稱的前綴。
protected String logPrefix;
// 指定 MyBatis 所用日志的具體實(shí)現(xiàn)删咱,未指定時(shí)將自動(dòng)查找屑埋。一般建議指定為slf4j或log4j
protected Class <? extends Log> logImpl;
// 指定VFS的實(shí)現(xiàn), VFS是mybatis提供的用于訪問(wèn)AS內(nèi)資源的一個(gè)簡(jiǎn)便接口
protected Class <? extends VFS> vfsImpl;
// MyBatis 利用本地緩存機(jī)制(Local Cache)防止循環(huán)引用(circular references)和加速重復(fù)嵌套查詢。 默認(rèn)值為 SESSION痰滋,這種情況下會(huì)緩存一個(gè)會(huì)話中執(zhí)行的所有查詢摘能。 若設(shè)置值為 STATEMENT,本地會(huì)話僅用在語(yǔ)句執(zhí)行上敲街,對(duì)相同 SqlSession 的不同調(diào)用將不會(huì)共享數(shù)據(jù)团搞。
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
// 當(dāng)沒(méi)有為參數(shù)提供特定的 JDBC 類型時(shí),為空值指定 JDBC 類型多艇。 某些驅(qū)動(dòng)需要指定列的 JDBC 類型逻恐,多數(shù)情況直接用一般類型即可,比如 NULL、VARCHAR 或 OTHER梢莽。
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
// 指定對(duì)象的哪個(gè)方法觸發(fā)一次延遲加載萧豆。
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
// 設(shè)置超時(shí)時(shí)間,它決定驅(qū)動(dòng)等待數(shù)據(jù)庫(kù)響應(yīng)的秒數(shù)昏名。默認(rèn)不超時(shí)
protected Integer defaultStatementTimeout;
// 為驅(qū)動(dòng)的結(jié)果集設(shè)置默認(rèn)獲取數(shù)量涮雷。
protected Integer defaultFetchSize;
// SIMPLE 就是普通的執(zhí)行器;REUSE 執(zhí)行器會(huì)重用預(yù)處理語(yǔ)句(prepared statements)轻局; BATCH 執(zhí)行器將重用語(yǔ)句并執(zhí)行批量更新洪鸭。
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
// 指定 MyBatis 應(yīng)如何自動(dòng)映射列到字段或?qū)傩浴?NONE 表示取消自動(dòng)映射;PARTIAL 只會(huì)自動(dòng)映射沒(méi)有定義嵌套結(jié)果集映射的結(jié)果集仑扑。 FULL 會(huì)自動(dòng)映射任意復(fù)雜的結(jié)果集(無(wú)論是否嵌套)览爵。
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
// 指定發(fā)現(xiàn)自動(dòng)映射目標(biāo)未知列(或者未知屬性類型)的行為。這個(gè)值應(yīng)該設(shè)置為WARNING比較合適
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
// settings下的properties屬性
protected Properties variables = new Properties();
// 默認(rèn)的反射器工廠,用于操作屬性镇饮、構(gòu)造器方便
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
// 對(duì)象工廠, 所有的類resultMap類都需要依賴于對(duì)象工廠來(lái)實(shí)例化
protected ObjectFactory objectFactory = new DefaultObjectFactory();
// 對(duì)象包裝器工廠,主要用來(lái)在創(chuàng)建非原生對(duì)象,比如增加了某些監(jiān)控或者特殊屬性的代理類
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
// 延遲加載的全局開(kāi)關(guān)蜓竹。當(dāng)開(kāi)啟時(shí),所有關(guān)聯(lián)對(duì)象都會(huì)延遲加載储藐。特定關(guān)聯(lián)關(guān)系中可通過(guò)設(shè)置fetchType屬性來(lái)覆蓋該項(xiàng)的開(kāi)關(guān)狀態(tài)俱济。
protected boolean lazyLoadingEnabled = false;
// 指定 Mybatis 創(chuàng)建具有延遲加載能力的對(duì)象所用到的代理工具。MyBatis 3.3+使用JAVASSIST
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
// MyBatis 可以根據(jù)不同的數(shù)據(jù)庫(kù)廠商執(zhí)行不同的語(yǔ)句钙勃,這種多廠商的支持是基于映射語(yǔ)句中的 databaseId 屬性蛛碌。
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
* 指定一個(gè)提供Configuration實(shí)例的類. 這個(gè)被返回的Configuration實(shí)例是用來(lái)加載被反序列化對(duì)象的懶加載屬性值. 這個(gè)類必須包含一個(gè)簽名方法static Configuration getConfiguration(). (從 3.2.3 版本開(kāi)始)
*/
protected Class<?> configurationFactory;
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
// mybatis插件列表
protected final InterceptorChain interceptorChain = new InterceptorChain();
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
// 類型注冊(cè)器, 用于在執(zhí)行sql語(yǔ)句的出入?yún)⒂成湟约癿ybatis-config文件里的各種配置比如<transactionManager type="JDBC"/><dataSource type="POOLED">時(shí)使用簡(jiǎn)寫(xiě), 后面會(huì)詳細(xì)解釋
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
protected final Set<String> loadedResources = new HashSet<String>();
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
/*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map<String, String> cacheRefMap = new HashMap<String, String>();
public Configuration(Environment environment) {
this();
this.environment = environment;
}
public Configuration() {
// 內(nèi)置別名注冊(cè)
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
... 省去不必要的getter/setter
public Class<? extends VFS> getVfsImpl() {
return this.vfsImpl;
}
public void setVfsImpl(Class<? extends VFS> vfsImpl) {
if (vfsImpl != null) {
this.vfsImpl = vfsImpl;
VFS.addImplClass(this.vfsImpl);
}
}
public ProxyFactory getProxyFactory() {
return proxyFactory;
}
public void setProxyFactory(ProxyFactory proxyFactory) {
if (proxyFactory == null) {
proxyFactory = new JavassistProxyFactory();
}
this.proxyFactory = proxyFactory;
}
/**
* @since 3.2.2
*/
public List<Interceptor> getInterceptors() {
return interceptorChain.getInterceptors();
}
public LanguageDriverRegistry getLanguageRegistry() {
return languageRegistry;
}
public void setDefaultScriptingLanguage(Class<?> driver) {
if (driver == null) {
driver = XMLLanguageDriver.class;
}
getLanguageRegistry().setDefaultDriverClass(driver);
}
public LanguageDriver getDefaultScriptingLanguageInstance() {
return languageRegistry.getDefaultDriver();
}
/** @deprecated Use {@link #getDefaultScriptingLanguageInstance()} */
@Deprecated
public LanguageDriver getDefaultScriptingLanuageInstance() {
return getDefaultScriptingLanguageInstance();
}
public MetaObject newMetaObject(Object object) {
return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
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) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
keyGenerators.put(id, keyGenerator);
}
public Collection<String> getKeyGeneratorNames() {
return keyGenerators.keySet();
}
public Collection<KeyGenerator> getKeyGenerators() {
return keyGenerators.values();
}
public KeyGenerator getKeyGenerator(String id) {
return keyGenerators.get(id);
}
public boolean hasKeyGenerator(String id) {
return keyGenerators.containsKey(id);
}
public void addCache(Cache cache) {
caches.put(cache.getId(), cache);
}
public Collection<String> getCacheNames() {
return caches.keySet();
}
public Collection<Cache> getCaches() {
return caches.values();
}
public Cache getCache(String id) {
return caches.get(id);
}
public boolean hasCache(String id) {
return caches.containsKey(id);
}
public void addResultMap(ResultMap rm) {
resultMaps.put(rm.getId(), rm);
checkLocallyForDiscriminatedNestedResultMaps(rm);
checkGloballyForDiscriminatedNestedResultMaps(rm);
}
public Collection<String> getResultMapNames() {
return resultMaps.keySet();
}
public Collection<ResultMap> getResultMaps() {
return resultMaps.values();
}
public ResultMap getResultMap(String id) {
return resultMaps.get(id);
}
public boolean hasResultMap(String id) {
return resultMaps.containsKey(id);
}
public void addParameterMap(ParameterMap pm) {
parameterMaps.put(pm.getId(), pm);
}
public Collection<String> getParameterMapNames() {
return parameterMaps.keySet();
}
public Collection<ParameterMap> getParameterMaps() {
return parameterMaps.values();
}
public ParameterMap getParameterMap(String id) {
return parameterMaps.get(id);
}
public boolean hasParameterMap(String id) {
return parameterMaps.containsKey(id);
}
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
public Collection<String> getMappedStatementNames() {
buildAllStatements();
return mappedStatements.keySet();
}
public Collection<MappedStatement> getMappedStatements() {
buildAllStatements();
return mappedStatements.values();
}
public Collection<XMLStatementBuilder> getIncompleteStatements() {
return incompleteStatements;
}
public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
incompleteStatements.add(incompleteStatement);
}
public Collection<CacheRefResolver> getIncompleteCacheRefs() {
return incompleteCacheRefs;
}
public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
incompleteCacheRefs.add(incompleteCacheRef);
}
public Collection<ResultMapResolver> getIncompleteResultMaps() {
return incompleteResultMaps;
}
public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
incompleteResultMaps.add(resultMapResolver);
}
public void addIncompleteMethod(MethodResolver builder) {
incompleteMethods.add(builder);
}
public Collection<MethodResolver> getIncompleteMethods() {
return incompleteMethods;
}
public MappedStatement getMappedStatement(String id) {
return this.getMappedStatement(id, true);
}
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.get(id);
}
public Map<String, XNode> getSqlFragments() {
return sqlFragments;
}
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
public void addMappers(String packageName, Class<?> superType) {
mapperRegistry.addMappers(packageName, superType);
}
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
public boolean hasMapper(Class<?> type) {
return mapperRegistry.hasMapper(type);
}
public boolean hasStatement(String statementName) {
return hasStatement(statementName, true);
}
public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.containsKey(statementName);
}
public void addCacheRef(String namespace, String referencedNamespace) {
cacheRefMap.put(namespace, referencedNamespace);
}
/*
* Parses all the unprocessed statement nodes in the cache. It is recommended
* to call this method once all the mappers are added as it provides fail-fast
* statement validation.
*/
protected void buildAllStatements() {
if (!incompleteResultMaps.isEmpty()) {
synchronized (incompleteResultMaps) {
// This always throws a BuilderException.
incompleteResultMaps.iterator().next().resolve();
}
}
if (!incompleteCacheRefs.isEmpty()) {
synchronized (incompleteCacheRefs) {
// This always throws a BuilderException.
incompleteCacheRefs.iterator().next().resolveCacheRef();
}
}
if (!incompleteStatements.isEmpty()) {
synchronized (incompleteStatements) {
// This always throws a BuilderException.
incompleteStatements.iterator().next().parseStatementNode();
}
}
if (!incompleteMethods.isEmpty()) {
synchronized (incompleteMethods) {
// This always throws a BuilderException.
incompleteMethods.iterator().next().resolve();
}
}
}
/*
* Extracts namespace from fully qualified statement id.
*
* @param statementId
* @return namespace or null when id does not contain period.
*/
protected String extractNamespace(String statementId) {
int lastPeriod = statementId.lastIndexOf('.');
return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null;
}
// Slow but a one time cost. A better solution is welcome.
protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
if (rm.hasNestedResultMaps()) {
for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
Object value = entry.getValue();
if (value instanceof ResultMap) {
ResultMap entryResultMap = (ResultMap) value;
if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values();
if (discriminatedResultMapNames.contains(rm.getId())) {
entryResultMap.forceNestedResultMaps();
}
}
}
}
}
}
// Slow but a one time cost. A better solution is welcome.
protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
for (Map.Entry<String, String> entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) {
String discriminatedResultMapName = entry.getValue();
if (hasResultMap(discriminatedResultMapName)) {
ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
if (discriminatedResultMap.hasNestedResultMaps()) {
rm.forceNestedResultMaps();
break;
}
}
}
}
}
protected static class StrictMap<V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
public StrictMap(String name, int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
this.name = name;
}
public StrictMap(String name, int initialCapacity) {
super(initialCapacity);
this.name = name;
}
public StrictMap(String name) {
super();
this.name = name;
}
public StrictMap(String name, Map<String, ? extends V> m) {
super(m);
this.name = name;
}
@SuppressWarnings("unchecked")
public V put(String key, V value) {
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key);
}
if (key.contains(".")) {
final String shortKey = getShortName(key);
if (super.get(shortKey) == null) {
super.put(shortKey, value);
} else {
super.put(shortKey, (V) new Ambiguity(shortKey));
}
}
return super.put(key, value);
}
public V get(Object key) {
V value = super.get(key);
if (value == null) {
throw new IllegalArgumentException(name + " does not contain value for " + key);
}
if (value instanceof Ambiguity) {
throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
+ " (try using the full name including the namespace, or rename one of the entries)");
}
return value;
}
private String getShortName(String key) {
final String[] keyParts = key.split("\\.");
return keyParts[keyParts.length - 1];
}
protected static class Ambiguity {
final private String subject;
public Ambiguity(String subject) {
this.subject = subject;
}
public String getSubject() {
return subject;
}
}
}
}
mybatis中所有環(huán)境配置、resultMap集合辖源、sql語(yǔ)句集合蔚携、插件列表、緩存克饶、加載的xml列表酝蜒、類型別名、類型處理器等全部都維護(hù)在Configuration中彤路。Configuration中包含了一個(gè)內(nèi)部靜態(tài)類StrictMap秕硝,它繼承于HashMap,對(duì)HashMap的裝飾在于增加了put時(shí)防重復(fù)的處理洲尊,get時(shí)取不到值時(shí)候的異常處理,這樣核心應(yīng)用層就不需要額外關(guān)心各種對(duì)象異常處理,簡(jiǎn)化應(yīng)用層邏輯奈偏。
從設(shè)計(jì)上來(lái)說(shuō)坞嘀,我們可以說(shuō)Configuration并不是一個(gè)thin類(也就是僅包含了屬性以及getter/setter),而是一個(gè)rich類惊来,它對(duì)部分邏輯進(jìn)行了封裝便于用戶直接使用,而不是讓用戶各自散落處理丽涩,比如addResultMap方法和getMappedStatement方法等等。
最新的完整mybatis每個(gè)配置屬性含義可參考http://www.mybatis.org/mybatis-3/zh/configuration.html。
從Configuration構(gòu)造器和protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();可以看出矢渊,所有我們?cè)趍ybatis-config和mapper文件中使用的類似int/string/JDBC/POOLED等字面常量最終解析為具體的java類型都是在typeAliasRegistry構(gòu)造器和Configuration構(gòu)造器執(zhí)行期間初始化的继准。下面我們來(lái)每塊分析