建造者模式是Java基本設(shè)計模式的一種,是經(jīng)常被使用到的老翘。比如在開發(fā)過程中經(jīng)常使用的lambok插件芹啥,在實體類上加上@Builder
就可以使用建造者模式方式構(gòu)建實體對象。這mybatis中同樣也使用到了這種設(shè)計模式铺峭,但是不是使用的lambok插件墓怀。在讀取配置文件的時候,需要讀取各種類型的標簽卫键,讓構(gòu)造器和標簽對應(yīng)傀履,可以將標簽的屬性及相關(guān)的值放到構(gòu)建器中,然后由構(gòu)建器構(gòu)建一個對象莉炉,這個對象中封裝了標簽的信息钓账,提供后續(xù)使用。
其實里面還使用到裝飾者模式絮宁,這篇文章先不說梆暮,后續(xù)出文再具體說裝飾者模式∩馨海可以劇透一下啦粹,在二級緩存中,使用裝飾器模式給二級緩存加各種功能窘游。
mybatis讀取配置
涉及到mybatis的配置文件不多唠椭,主要就是mybatis-config.xml
核心配置文件,然后就是mapper.xml
映射文件忍饰。mybatis在讀取配置文件的時候也是按照這個順序贪嫂,先讀核心配置文件,將核心配置文件中的信息封裝到核心配置類Configuration
中喘批。核心配置文件中配置了mapper.xml
的位置撩荣,然后根據(jù)配置的路徑讀取mapper.xml
铣揉。整個過程思路很清晰,其中涉及到三個核心的類XMLConfigBuilder
餐曹、XMLMapperBuilder
逛拱、XMLStatementBuilder
。來看下面的圖台猴。
從圖里面可以看的出來朽合,就是將相關(guān)配置文件的信息經(jīng)過封裝,然后統(tǒng)一放到Configuration
配置類中饱狂。
XMLConfigBuilder源碼
接下來看XMLConfigBuilder
加載核心配置文件的代碼曹步,這個過程其實不難,流程也很清晰休讳。
parse
parse
方法是入口讲婚,在配置文件被讀入到parser
解析器后,會調(diào)用這個方法開始解析工作俊柔。
//配置類開始解析的方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
//設(shè)置開始解析的標識筹麸,防止重復(fù)解析
parsed = true;
//調(diào)用解析核心配置文件的方法
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
上面這個方法首先是判斷當前配置文件是否已經(jīng)在解析了,保證同一個文件不會被重復(fù)的解析雏婶。
parseConfiguration
這里就是開始真正的解析配置文件中的內(nèi)容啦物赶。
//解析mybatis核心配置文件mybatis-config.xml,configuration是此文件的根目錄
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//獲取properties標簽信息
propertiesElement(root.evalNode("properties"));
//獲取settings標簽設(shè)置信息
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
//獲取別名信息
typeAliasesElement(root.evalNode("typeAliases"));
//獲取插件信息
pluginElement(root.evalNode("plugins"));
//objectFactory留晚、objectWrapperFactory酵紫、reflectorFactory信息(一般項目上用不上這些信息)
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//設(shè)置配置信息
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//解析環(huán)境信息(數(shù)據(jù)庫信息+事務(wù)信息)
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//類型處理器
typeHandlerElement(root.evalNode("typeHandlers"));
//mapper.xml文件
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
流程很清晰,在之前的基礎(chǔ)文章中也寫過在核心配置文件中的主要標簽错维,這里基本都體現(xiàn)到了奖地,就是對這些主要的標簽進行依次的解析。(核心標簽的的文章可以參考這篇文章:ORM框架之Mybatis:基礎(chǔ)配置)
這個方法的流程就不介紹了赋焕,上面的源碼中也有注釋鹉动。
settingsAsProperties
這里是mybatis-config.xml
核心配置文件中<settings>
標簽的解析。
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();//獲取所有子標簽信息
// Check that all settings are known to the configuration class
//檢查settings里面加的配置信息是否是可配置項
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
這里就是讀取<settings>
標簽下的所有子標簽宏邮,并將子標簽信息封裝到pros
中返回。讀取的過程是在第五行代碼缸血。
public Properties getChildrenAsProperties() {
Properties properties = new Properties();
for (XNode child : getChildren()) {
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
}
遍歷子標簽蜜氨,讀取子標簽的name
和value
屬性,然后封裝捎泻。沒有復(fù)雜的邏輯飒炎。
到這里這個<settings>
標簽下的所有內(nèi)容就都讀取成功啦。
settingsElement
這個方法很實用的笆豁,平時在開發(fā)過程中都會設(shè)置各種配置參數(shù)郎汪,配置參數(shù)也有對應(yīng)的默認值赤赊,什么時候需要配置,什么時候不需要配置使用默認就好煞赢。都是在這里面啦抛计。
//將settings標簽的子標簽配置信息設(shè)置到Configuration內(nèi),部分配置信息有對應(yīng)的默認值
private void settingsElement(Properties props) throws Exception {
//autoMappingBehavior,默認值PARTIAL
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
//autoMappingUnknownColumnBehavior,默認值NONE
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
//cacheEnabled,二級緩存全局開關(guān)照筑,默認值true
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
//proxyFactory吹截,代理工廠,無默認值
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
//lazyLoadingEnabled凝危,是否可以延遲加載波俄,默認值false
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
//aggressiveLazyLoading,侵略性懶加載屬性蛾默,也就是按需加載的意思懦铺,默認值false
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
//multipleResultSetsEnabled,默認值true
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
//useColumnLabel支鸡,默認值true
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
//useGeneratedKeys冬念,使用生成鍵,默認值false
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
//defaultExecutorType苍匆,默認執(zhí)行器類型刘急,默認值是SIMPLE,可選值SIMPLE, REUSE, BATCH
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
//defaultStatementTimeout浸踩,默認值null
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
//defaultFetchSize叔汁,默認值null
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
//mapUnderscoreToCamelCase,駝峰規(guī)則自動映射检碗,默認是false
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
//safeRowBoundsEnabled据块,默認值false
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
//localCacheScope,緩存返回折剃,默認是SESSION另假,可選值SESSION,STATEMENT
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
//jdbcTypeForNull,默認值OTHER怕犁,可選值很多边篮,參考JdbcType類
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
//lazyLoadTriggerMethods,懶加載觸發(fā)的方法奏甫,equals,clone,hashCode,toString
//如果重寫了這些方法戈轿,不涉及關(guān)聯(lián)對象,懶加載也不會被觸發(fā)
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
//safeResultHandlerEnabled阵子,默認值true
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
//defaultScriptingLanguage思杯,默認腳本語言,無默認值
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
//defaultEnumTypeHandler挠进,枚舉類型處理器色乾,無默認值
@SuppressWarnings("unchecked")
Class<? extends TypeHandler> typeHandler = (Class<? extends TypeHandler>) resolveClass(props.getProperty("defaultEnumTypeHandler"));
configuration.setDefaultEnumTypeHandler(typeHandler);
//callSettersOnNulls誊册,默認值false
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
//useActualParamName,默認值true
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
//returnInstanceForEmptyRow暖璧,默認值false
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
//logPrefix案怯,日志前綴,無默認值
configuration.setLogPrefix(props.getProperty("logPrefix"));
//logImpl漆撞,日志實現(xiàn)類殴泰,無默認值
@SuppressWarnings("unchecked")
Class<? extends Log> logImpl = (Class<? extends Log>) resolveClass(props.getProperty("logImpl"));
configuration.setLogImpl(logImpl);
//configurationFactory,無默認值
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
里面都是將剛才讀取的配置信息放到configuration
中浮驳,沒有對應(yīng)值的賦予默認值(缺省值)悍汛。
其他標簽的解析就不具體去說了,基本都是差不多的套路至会。
本文作者:程序猿楊鮑
版權(quán)歸作者所有离咐,轉(zhuǎn)載請注明出處