前言
從2019年7月4日開始,每天盡量抽一個小時出來,到了2019年10月9日總算把Mybatis3.5.1的源碼給讀完了,雖然是很累,但確實收獲不少,至少比起看教程,收獲更多。
因為我是直接把說明寫到代碼上的,所以我不會再多說明某個類,某個方法。所以本博客只適合那些正在看Mybatis源碼的讀者,幫助他們更好的理解
Mybatis3.5.1源碼分析
- Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源碼解析
- Mybatis-Configuration源碼解析
- Mybatis-事務(wù)對象源碼解析
- Mybatis-數(shù)據(jù)源源碼解析
- Mybatis緩存策略源碼解析
- Mybatis-DatabaseIdProvider源碼解析
- Mybatis-TypeHandler源碼解析
- Mybatis-Reflector源碼解析
- Mybatis-ObjectFactory,ObjectWrapperFactory源碼分析
- Mybatis-Mapper各類標簽封裝類源碼解析
- Mybatis-XMLMapperBuilder,XMLStatmentBuilder源碼分析
- Mybatis-MapperAnnotationBuilder源碼分析
- [Mybatis-MetaObject,MetaClass源碼解析]http://www.itdecent.cn/p/f51fa552f30a)
- Mybatis-LanguageDriver源碼解析
- Mybatis-SqlSource源碼解析
- Mybatis-SqlNode源碼解析
- Mybatis-KeyGenerator源碼解析
- Mybatis-Executor源碼解析
- Mybatis-ParameterHandler源碼解析
- Mybatis-StatementHandler源碼解析
- Mybatis-DefaultResultSetHandler(一)源碼解析
- Mybatis-DefaultResultSetHandler(二)源碼解析
- Mybatis-ResultHandler,Cursor,RowBounds 源碼分析
- Mybatis-MapperProxy源碼解析
- Mybatis-SqlSession源碼解析
- Mybatis-Interceptor源碼解析
SqlSessionFactoryBuilder
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.session;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
/**
* Builds {@link SqlSession} instances.
* 利用XML或者Java編碼獲得資源來構(gòu)建SqlSessionFactory
* @author Clinton Begin
*/
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
/**
*
* @param inputStream mybatis-config.xml配置文件流
*/
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
/**
* 新建一個對mybatis的XML配置文件進行解析的解析器對應(yīng)配置文件先進性解析封裝
* @param inputStream 配置文件文件流
* @param environment 加載哪種環(huán)境(開發(fā)環(huán)境/生產(chǎn)環(huán)境),包括數(shù)據(jù)源和事務(wù)管理器
* @param properties 屬性配置文件,那些屬性可以用${propName}語法形式多次用在配置文件中
* @return
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//新建一個對mybatis的XML配置文件進行解析的解析器
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//parse:對Mybatis配置文件xml中的標簽信息進行解析封裝,然后添加到Mybatis全局配置信息中,返回Mybatis全局配置信息對象
return build(parser.parse());
} catch (Exception e) {
//包裝解析異常,進行更加具體的描述
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
//重置異常上下文實例
ErrorContext.instance().reset();
try {
//關(guān)閉配置文件文件流
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
BaseBuilder
/**
* 主要提供{@link #configuration},{@link #typeHandlerRegistry},{@link #typeHandlerRegistry}的處理。
* @author Clinton Begin
*/
public abstract class BaseBuilder {
/**
* Mybatis的全局配置信息
*/
protected final Configuration configuration;
/**
* 類型別名注冊器
*/
protected final TypeAliasRegistry typeAliasRegistry;
/**
* 類型處理器的注冊器
*/
protected final TypeHandlerRegistry typeHandlerRegistry;
/**
* 從{@link @configuration}中獲取類型別名注冊器和類型處理器的注冊器賦值
* 給{@link #typeAliasRegistry}和{@link #typeHandlerRegistry}
* @param configuration mybatsi全局配置信息
*/
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
/**
* 獲取 Mybatis的配置信息 {@link #configuration}
* @return
*/
public Configuration getConfiguration() {
return configuration;
}
/**
* 如果{@link @regex}為null,就用{@link @defaultValue}
*/
protected Pattern parseExpression(String regex, String defaultValue) {
return Pattern.compile(regex == null ? defaultValue : regex);
}
/**
* 如果{@link @value}為null,就返回{@link @defaultVale}
*/
protected Boolean booleanValueOf(String value, Boolean defaultValue) {
return value == null ? defaultValue : Boolean.valueOf(value);
}
/**
* 如果{@link @value}為null,就返回{@link @defaultVale}
*/
protected Integer integerValueOf(String value, Integer defaultValue) {
return value == null ? defaultValue : Integer.valueOf(value);
}
/**
* 將{@link @value}以','分隔的形式轉(zhuǎn)換成{@link Set},如果{@link @value}為null,就用{@link @defaultValue}
*/
protected Set<String> stringSetValueOf(String value, String defaultValue) {
value = value == null ? defaultValue : value;
return new HashSet<>(Arrays.asList(value.split(",")));
}
/**
* 找出對應(yīng){@link @alias}的{@link JdbcType},若{@link @alias}為null返回null,若沒有找到就會拋出{@link BuilderException}
*/
protected JdbcType resolveJdbcType(String alias) {
if (alias == null) {
return null;
}
try {
return JdbcType.valueOf(alias);
} catch (IllegalArgumentException e) {
throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
}
}
/**
* 找出對應(yīng){@link @alias}的{@link ResultSetType},若{@link @alias}為null返回null,若沒有找到就會拋出{@link BuilderException}
*/
protected ResultSetType resolveResultSetType(String alias) {
if (alias == null) {
return null;
}
try {
return ResultSetType.valueOf(alias);
} catch (IllegalArgumentException e) {
throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e);
}
}
/**
* 找出對應(yīng){@link @alias}的{@link ParameterMode},若{@link @alias}為null返回null,若沒有找到就會拋出{@link BuilderException},
* <p>ParameterMode: {@link ParameterMode#IN},{@link ParameterMode#OUT},{@link ParameterMode#INOUT}</p>
*/
protected ParameterMode resolveParameterMode(String alias) {
if (alias == null) {
return null;
}
try {
return ParameterMode.valueOf(alias);
} catch (IllegalArgumentException e) {
throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e);
}
}
/**
* 創(chuàng)建{@link @alias}的實例,{@link @alias}為null返回null,若在創(chuàng)建拋出任何異常信息都會封裝到{@link BuilderException}拋出
*/
protected Object createInstance(String alias) {
Class<?> clazz = resolveClass(alias);
if (clazz == null) {
return null;
}
try {
return resolveClass(alias).newInstance();
} catch (Exception e) {
throw new BuilderException("Error creating instance. Cause: " + e, e);
}
}
/**
* 通過類別名注冊器typeAliasRegistry解析出對應(yīng)的類 實際調(diào)用{@link #resolveAlias(String)},返回類實例
*/
protected <T> Class<? extends T> resolveClass(String alias) {
if (alias == null) {
return null;
}
try {
return resolveAlias(alias);
} catch (Exception e) {
throw new BuilderException("Error resolving class. Cause: " + e, e);
}
}
/**
* 構(gòu)建TypeHandler,不沒有注冊到{@link #typeHandlerRegistry},只是返回TypeHandler實例對象,實際調(diào)用{@link #resolveTypeHandler(Class, Class)}
* @param javaType 可以不傳的,不傳就用typeHandlerAlias的無參構(gòu)造方法,傳就嘗試調(diào)用帶有接收javaType的構(gòu)造方法[p:即使調(diào)用失敗也不會拋出異常,而是調(diào)用無參構(gòu)造方法]
* @param typeHandlerAlias 若null,該方法直接返回null;若傳入的包+類沒有繼承{@link TypeHandler}會拋出{@link BuilderException}
*/
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) {
if (typeHandlerAlias == null) {
return null;
}
//獲取typeHandlerAlias對應(yīng)的類
Class<?> type = resolveClass(typeHandlerAlias);
if (type != null && !TypeHandler.class.isAssignableFrom(type)) {
throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface");
}
@SuppressWarnings("unchecked") // already verified it is a TypeHandler 已經(jīng)嚴重type是TypeHandler類。
Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type;
return resolveTypeHandler(javaType, typeHandlerType);
}
/**
* 構(gòu)建TypeHandler,沒有注冊到{@link #typeHandlerRegistry},只是返回TypeHandler實例對象
* @param javaType 可以不傳的,但是不傳,也是可以的,但是不建議。
* @param typeHandlerType 若為null,該方法直接返回null
* @return
*/
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
if (typeHandlerType == null) {
return null;
}
// javaType ignored for injected handlers see issue #746 for full detail
//先從緩存Map中獲取
TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
if (handler == null) {
// not in registry, create a new one
//實例化一個新的typeHandler對象
handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
}
return handler;
}
/**
* 通過類別名注冊器typeAliasRegistry找出對應(yīng)的類
*/
protected <T> Class<? extends T> resolveAlias(String alias) {
return typeAliasRegistry.resolveAlias(alias);
}
}
XMLConfigBuilder
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.builder.xml;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.loader.ProxyFactory;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.io.VFS;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.AutoMappingBehavior;
import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.LocalCacheScope;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.JdbcType;
/**
* 對mybatis的XML配置文件進行解析的類
* https://blog.csdn.net/fageweiketang/article/details/80794847
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
/**
* Mybatis運行時所用的數(shù)據(jù)庫環(huán)境ID,一般從 <environments/> 的 default 屬性中獲取
*/
private String environment;
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
public XMLConfigBuilder(Reader reader) {
this(reader, null, null);
}
public XMLConfigBuilder(Reader reader, String environment) {
this(reader, environment, null);
}
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
public XMLConfigBuilder(InputStream inputStream) {
this(inputStream, null, null);
}
public XMLConfigBuilder(InputStream inputStream, String environment) {
this(inputStream, environment, null);
}
/**
*
* @param inputStream 配置文件InputStream
* @param environment 加載哪種環(huán)境(開發(fā)環(huán)境/生產(chǎn)環(huán)境),包括數(shù)據(jù)源和事務(wù)管理器
* @param props 屬性配置文件,那些屬性可以用${propName}語法形式多次用在配置文件中
*/
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
//XPathParser:XML解析器
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
/**
*
* @param parser Mybatis配置文件xml對應(yīng)的XML解析器
* @param environment 加載哪種環(huán)境(開發(fā)環(huán)境/生產(chǎn)環(huán)境),包括數(shù)據(jù)源和事務(wù)管理器
* @param props 屬性配置文件,那些屬性可以用${propName}語法形式多次用在配置文件中
*/
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//新建一個Mybatis全局配置信息類對象
super(new Configuration());
//設(shè)置錯誤報文實例的資源引用
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);//設(shè)置屬性文件的變量到配置類configuration
this.parsed = false;//parse表示是否已經(jīng)解析了
this.environment = environment;
this.parser = parser;//XML
}
/**
* 對Mybatis配置文件xml中的標簽信息進行解析封裝,然后添加到Mybatis全局配置信息中
* @return Mybatis全局配置信息對象
*/
public Configuration parse() {
if (parsed) {
//每個XMLConfig Builder只能使用一次;
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//對應(yīng)<configuation>節(jié)點
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
/**
* 解析mybatis-config.xml所有標簽信息,并實例化標簽對應(yīng)的對象,配置進 {@link XMLConfigBuilder#configuration} 里
* @param root
*/
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);
}
}
/**
* 讀取<setting></setting>便簽信息,每個<setting/>的name屬性對應(yīng)著Configuration的屬性變量名,
* 該放會檢驗<setting/>標簽的name屬性能不能在Configuration查找出來,如果查找不出來,拋出異常
*/
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
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {//這里就是判斷有沒有對應(yīng)的屬性在configuration里,沒有就拋出異常
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
/**
* 加載虛擬文件系統(tǒng)配置,讀取服務(wù)器資源
* VFS含義是虛擬文件系統(tǒng);主要是通過程序能夠方便讀取本地文件系統(tǒng)、FTP文件系統(tǒng)等系統(tǒng)中的文件資源
* Mybatis中提供了VFS這個配置,主要是通過該配置可以加載自定義的虛擬文件系統(tǒng)應(yīng)用程序
*/
private void loadCustomVfs(Properties props) throws ClassNotFoundException {
String value = props.getProperty("vfsImpl");
if (value != null) {
String[] clazzes = value.split(",");
for (String clazz : clazzes) {
if (!clazz.isEmpty()) {
@SuppressWarnings("unchecked")
Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
configuration.setVfsImpl(vfsImpl);
}
}
}
}
/**
* 制定日志的實現(xiàn)(log4j等)
*/
private void loadCustomLogImpl(Properties props) {
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));//找出logImpl對應(yīng)的日志類
configuration.setLogImpl(logImpl);
}
/**
* 類型別名元素
* @param parent <typeAliases/>
*/
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {//如果是包名
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);//掃描包下的所有符合的類
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);//通過類加載器加載對應(yīng)的來
//注冊別名到typeAliasRegistry
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
/**
* <plugins/>元素,每個插件就是mybatis的攔截器。
* @param parent <plugins/>標簽
*/
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();//加載對應(yīng)的攔截類,并new一個實例
interceptorInstance.setProperties(properties);//加上<plugin/>標簽內(nèi)的<property>標簽信息
configuration.addInterceptor(interceptorInstance);//注冊到configuration的攔截器鏈中
}
}
}
/**
* ObjectFactory 標簽 -- [對象工廠 {@link ObjectFactory}]
* @param context
* @throws Exception
*/
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties properties = context.getChildrenAsProperties();
ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();//加載對應(yīng)的ObjectFactory類,并new一個實例
factory.setProperties(properties);//加上<ObjectFactory/>標簽內(nèi)的<property>標簽信息
configuration.setObjectFactory(factory);//注冊到objectFactory
}
}
/**
* ObjectWrapperFactory 標簽 -- [對象包裝類工廠{@link ObjectWrapperFactory}]
* @param context
* @throws Exception
*/
private void objectWrapperFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();//加載對應(yīng)的ObjectWrapperFactory類,并new一個實例
configuration.setObjectWrapperFactory(factory);//注冊到objectWrapperFactory
}
}
/**
* reflectorFactory標簽 -- [反射信息類工廠{@link ReflectorFactory}]
* @param context
* @throws Exception
*/
private void reflectorFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();//加載對應(yīng)的ReflectorFactory類,并new一個實例
configuration.setReflectorFactory(factory);//注冊到ReflectorFactory
}
}
/**
* 讀取<Properties>標簽的屬性變量,以及合并configuration的屬性變量,然后重新賦值到parser和configuration的屬性變量中。
*/
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
Properties defaults = context.getChildrenAsProperties();
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) {//resource屬性和url屬性在<properties>標簽中只能指定一個
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
//加載對應(yīng)路徑的屬性
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
//加載configuration的屬性
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);//設(shè)置解析器的屬性變量
configuration.setVariables(defaults);//設(shè)置Configuration的屬性變量
}
}
/**
* 將<setting/>標簽的設(shè)置全部設(shè)置進去Configuation
*/
private void settingsElement(Properties props) {
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
/**
* <Environments/>標簽元素
* <p>
* 獲取<Environments/>下的default屬性取得當前Mybatis所需要的環(huán)境ID并賦值給 {@link XMLConfigBuilder#environment},
* 然后獲取<environment/>的id屬性(環(huán)境ID)并賦值給{@link @id},判斷{@link @id}是不是{@link XMLConfigBuilder#environment},
* 是就做以下操作
* <ol>
* <li>獲取環(huán)境ID</li>
* <li>獲取事務(wù)管理器工廠的實例對象,賦值給 {@link @txFactory}</li>
* <li>獲取數(shù)據(jù)庫數(shù)據(jù)源工廠的實例對象{@link @dsFactory},然后從{@link @dsFactory} 獲取數(shù)據(jù)源,賦值給 {@link @dataSoure}</li>
* <li>傳入{@link @txFactory},{@link @dataSoure},{@link @id} 構(gòu)建 {@link Environment} 傳入 {@link XMLConfigBuilder#configuration}</li>
* </ol>
* </p>
* @param context
* @throws Exception
*/
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");//默認環(huán)境ID,指<environment/>的ID屬性
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");//環(huán)境ID
//只有符合指定的默認環(huán)境ID的環(huán)境ID才會被解析
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//事務(wù)管理器工廠
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//數(shù)據(jù)源工廠
DataSource dataSource = dsFactory.getDataSource();//取出數(shù)據(jù)源
//構(gòu)建環(huán)境
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
/**
* <databaseIdProvider/> 解析?!緮?shù)據(jù)庫廠商標識】
* <p>
* MyBatis 可以根據(jù)不同的數(shù)據(jù)庫廠商執(zhí)行不同的語句,這種多廠商的支持是基于映射語句中的 databaseId 屬性。
* MyBatis 會加載不帶 databaseId 屬性和帶有匹配當前數(shù)據(jù)庫 databaseId 屬性的所有語句。
* 如果同時找到帶有 databaseId 和不帶 databaseId 的相同語句,則后者會被舍棄。
* 為支持多廠商特性設(shè)置 databaseIdProvider
* </p>
* <p>
* Mybatis內(nèi)置了一個 {@link org.apache.ibatis.mapping.VendorDatabaseIdProvider},對type屬性設(shè)置別名'DB_VENDOR'即可使用。
* </p>
* 參考文檔
* <ol>
* <li>https://www.cnblogs.com/hellowhy/p/9676037.html</li>
* <li>http://www.mybatis.org/mybatis-3/zh/configuration.html#databaseIdProvider</li>
* </ol>
*/
private void databaseIdProviderElement(XNode context) throws Exception {
DatabaseIdProvider databaseIdProvider = null;
if (context != null) {
String type = context.getStringAttribute("type");
// awful patch to keep backward compatibility
//糟糕的補丁保持向后兼容性;可能由于之前的版本不是DB_VENDOR,所以加上這段代碼。
if ("VENDOR".equals(type)) {
type = "DB_VENDOR";//{@link VendorDatabaseIdProvider}的別名
}
Properties properties = context.getChildrenAsProperties();
databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
databaseIdProvider.setProperties(properties);
}
/**
* 從environment中獲取數(shù)據(jù)源,交給databaseIdProvider獲取數(shù)據(jù)庫ID。
* 數(shù)據(jù)ID,其實就是數(shù)據(jù)庫名,或者properties對應(yīng)key的value(這個需要用戶去配置)
*/
Environment environment = configuration.getEnvironment();
if (environment != null && databaseIdProvider != null) {
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
configuration.setDatabaseId(databaseId);
}
}
/**
* <environment/>下的<dataSource />標簽信息,返回事務(wù)工廠 {@link TransactionFactory}
* <ul>
* <li> type= 'JDBC' ,返回 {@link org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory}</li>
* <li> type= 'MANAGED' ,返回 {@link org.apache.ibatis.transaction.managed.ManagedTransactionFactory}</li>
* <li> type='包+類名',返回對應(yīng)類實例 {@link TransactionFactory} ,該類必須保證繼承了 {@link TransactionFactory} 接口</li>
* </ul>
* @throws BuilderException 沒有設(shè)置<transactionManager />就會拋出異常
*/
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
/**
* <environment/>下的<transactionManager />標簽信息,返回數(shù)據(jù)源工廠 {@link DataSourceFactory}
* <ul>
* <li> type= 'JNDI' ,返回 {@link org.apache.ibatis.datasource.jndi.JndiDataSourceFactory}</li>
* <li> type= 'POOLED' ,返回 {@link org.apache.ibatis.datasource.pooled.PooledDataSourceFactory}</li>
* <li> type= 'UNPOOLED' ,返回 {@link org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory}</li>
* <li> type='包+類名',返回對應(yīng)類實例 {@link DataSourceFactory} ,該類必須保證繼承了 {@link DataSourceFactory} 接口</li>
* </ul>
* @throws BuilderException 沒有設(shè)置<transactionManager />就會拋出異常
*/
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
/**
*<typeHandler/>解釋
* <p>
* 調(diào)用{#typeHandlerRegistry}注冊TypeHandler。就算javaType,jdbcType都是null,都能注冊得到TypeHandler,只不過javaType,jdbcType都是null
* 。但是我不建議這樣做、
* </p>
* @param parent
*/
private void typeHandlerElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//掃描包,找出尋找TypeHandler的類
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
if (javaTypeClass != null) {
//
if (jdbcType == null) {
//這種情況下注冊,需要typeHandlerClass加上MappedJdbcTypes注解去指定jdbcType,否則,將會把jdbcType當作null進行注冊
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
/**
* 沒有得到j(luò)avaTypeClass時,連jdbcType都不會判斷,因為typeHandlerRegistery已經(jīng)進行處理了:
* 1、從typeHandlerClass中嘗試獲取MappedTypes注解的配置javaType,然后如果拿不到,就看看有沒有繼承TypeReference,從
* TypeReference的泛型T中拿到聲明的類型作為javaType,還是拿不到就用null
* 2、從typeHandlerClass中嘗試獲取MappedJdbcTypes注解信息的jdbcType,拿不到就用null
* 就是說就算拿不到j(luò)avaType,jdbcType也可以進行注冊TypeHandler,只不是javaType,jdbcType為null。
*/
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
/**
* <mapper/>
*/
private void mapperElement(XNode parent) throws Exception {
//如果有配置mappers標簽
if (parent != null) {
//遍歷mappers標簽下的所有package標簽和mapper標簽
for (XNode child : parent.getChildren()) {
//如果child為package標簽
if ("package".equals(child.getName())) {
//獲取package標簽指定的映射包名路徑(相對于類路徑的資源引用)
String mapperPackage = child.getStringAttribute("name");
//將映射包名路徑添加到mybatis全局配置信息中
configuration.addMappers(mapperPackage);
//這里else表示的是mapper標簽
} else {
//resource表示使用相對于類路徑的資源引用
String resource = child.getStringAttribute("resource");
//url表示使用完全限定資源定位符(URL)
String url = child.getStringAttribute("url");
//使用映射器接口實現(xiàn)類的完全限定類名
String mapperClass = child.getStringAttribute("class");
//如果配置了resource 但是 url以及mapperClass沒有配置
if (resource != null && url == null && mapperClass == null) {
//設(shè)置錯誤報文實例的資源引用為resource
ErrorContext.instance().resource(resource);
//獲取resource文件輸入流
InputStream inputStream = Resources.getResourceAsStream(resource);
//新建一個XML映射文件構(gòu)建器
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
/**
* 解析Mapper.xml,解析mapper標簽下的所有標簽,并對解析出來的標簽信息加以封裝,
* 然后添加到Mybatis全局配置信息中。然后重新解析Mybatis全局配置信息中未能完成解析的
* ResultMap標簽信息,CacheRef標簽信息,DML標簽信息
*/
mapperParser.parse();
//如果配置了url但是resource以及mapperClass沒有配置
} else if (resource == null && url != null && mapperClass == null) {
//設(shè)置錯誤報文實例的資源引用為url
ErrorContext.instance().resource(url);
//獲取url文件輸入流
InputStream inputStream = Resources.getUrlAsStream(url);
//新建一個XML映射文件構(gòu)建器
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
/**
* 解析Mapper.xml,解析mapper標簽下的所有標簽,并對解析出來的標簽信息加以封裝,
* 然后添加到Mybatis全局配置信息中。然后重新解析Mybatis全局配置信息中未能完成解析的
* ResultMap標簽信息,CacheRef標簽信息,DML標簽信息
*/
mapperParser.parse();
//如果配置了mapperClass但是resource以及url沒有配置
} else if (resource == null && url == null && mapperClass != null) {
//加載mapperClass對應(yīng)的java類
Class<?> mapperInterface = Resources.classForName(mapperClass);
//將mapperInterface加入到mapperRegistry中
configuration.addMapper(mapperInterface);
} else {
//如果把url,resource,mapperClass都配置,就會拋出異常
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
/**
* 是否是指定的環(huán)境
* <p>
* 通過 {@link XMLConfigBuilder#environment} 是否等于 {@link @id},只有{@link XMLConfigBuilder#environment}
* 或者{@link @id}其中一個為null,都會拋出 {@link BuilderException}
* </p>
* @param id 環(huán)境ID
*/
private boolean isSpecifiedEnvironment(String id) {
if (environment == null) {
throw new BuilderException("No environment specified.");
} else if (id == null) {
throw new BuilderException("Environment requires an id attribute.");
} else if (environment.equals(id)) {
return true;
}
return false;
}
}
XPathParser
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.parsing;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.apache.ibatis.builder.BuilderException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* XML解析器
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public class XPathParser {
private final Document document;
private boolean validation;
/**
* DTD對象
*/
private EntityResolver entityResolver;
private Properties variables;
private XPath xpath;
public XPathParser(String xml) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document) {
commonConstructor(false, null, null);
this.document = document;
}
public XPathParser(String xml, boolean validation) {
commonConstructor(validation, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader, boolean validation) {
commonConstructor(validation, null, null);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream, boolean validation) {
commonConstructor(validation, null, null);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document, boolean validation) {
commonConstructor(validation, null, null);
this.document = document;
}
public XPathParser(String xml, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = document;
}
public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = document;
}
public void setVariables(Properties variables) {
this.variables = variables;
}
public String evalString(String expression) {
return evalString(document, expression);
}
public String evalString(Object root, String expression) {
String result = (String) evaluate(expression, root, XPathConstants.STRING);
result = PropertyParser.parse(result, variables);
return result;
}
public Boolean evalBoolean(String expression) {
return evalBoolean(document, expression);
}
public Boolean evalBoolean(Object root, String expression) {
return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN);
}
public Short evalShort(String expression) {
return evalShort(document, expression);
}
public Short evalShort(Object root, String expression) {
return Short.valueOf(evalString(root, expression));
}
public Integer evalInteger(String expression) {
return evalInteger(document, expression);
}
public Integer evalInteger(Object root, String expression) {
return Integer.valueOf(evalString(root, expression));
}
public Long evalLong(String expression) {
return evalLong(document, expression);
}
public Long evalLong(Object root, String expression) {
return Long.valueOf(evalString(root, expression));
}
public Float evalFloat(String expression) {
return evalFloat(document, expression);
}
public Float evalFloat(Object root, String expression) {
return Float.valueOf(evalString(root, expression));
}
public Double evalDouble(String expression) {
return evalDouble(document, expression);
}
public Double evalDouble(Object root, String expression) {
return (Double) evaluate(expression, root, XPathConstants.NUMBER);
}
public List<XNode> evalNodes(String expression) {
return evalNodes(document, expression);
}
public List<XNode> evalNodes(Object root, String expression) {
List<XNode> xnodes = new ArrayList<>();
NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
xnodes.add(new XNode(this, nodes.item(i), variables));
}
return xnodes;
}
public XNode evalNode(String expression) {
return evalNode(document, expression);
}
public XNode evalNode(Object root, String expression) {
Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
if (node == null) {
return null;
}
return new XNode(this, node, variables);
}
private Object evaluate(String expression, Object root, QName returnType) {
try {
return xpath.evaluate(expression, root, returnType);
} catch (Exception e) {
throw new BuilderException("Error evaluating XPath. Cause: " + e, e);
}
}
/**
* 檢驗mybatis-config.xml,并將mybatis-config.xml轉(zhuǎn)換成Doucument
*/
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);//若true解析器將DTD驗證被解析的文檔
factory.setNamespaceAware(false);//若true解析器將提供對 XML 名稱空間的支持
factory.setIgnoringComments(true);//若true忽略注釋
factory.setIgnoringElementContentWhitespace(false);//若true,解析器在解析 XML 文檔時,必須刪除元素內(nèi)容中的空格
factory.setCoalescing(false);//若true,解析器將把 CDATA 節(jié)點轉(zhuǎn)換為 Text 節(jié)點,并將其附加到相鄰(如果有)的 Text 節(jié)點
factory.setExpandEntityReferences(true);//若true,的解析器將擴展實體引用節(jié)點
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);//設(shè)置DTD
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
/**
* 通用的構(gòu)造方法
* 使用外部傳入的參數(shù),初始化成員變量;
* 構(gòu)造XPathFactory對象,獲得Xpath的對象
* @param validation 設(shè)置解析xml時是否對它進行校驗。
* @param variables
* @param entityResolver
*/
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
}