前言
从2019年7月4日开始,每天尽量抽一个小时出来,到了2019年10月9日总算把Mybatis3.5.1的源码给读完了,虽然是很累,但确实收获不少,至少比起看教程,收获更多。
因为我是直接把说明写到代码上的,所以我不会再多说明某个类,某个方法。所以本博客只适合那些正在看Mybatis源码的读者,帮助他们更好的理解
Mybatis3.5.1源码分析
- Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
- Mybatis-Configuration源码解析
- Mybatis-事务对象源码解析
- Mybatis-数据源源码解析
- Mybatis缓存策略源码解析
- Mybatis-DatabaseIdProvider源码解析
- Mybatis-TypeHandler源码解析
- Mybatis-Reflector源码解析
- Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
- Mybatis-Mapper各类标签封装类源码解析
- Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
- Mybatis-MapperAnnotationBuilder源码分析
- [Mybatis-MetaObject,MetaClass源码解析]https://www.jianshu.com/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编码获得资源来构建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配置文件进行解析的解析器对应配置文件先进性解析封装
* @param inputStream 配置文件文件流
* @param environment 加载哪种环境(开发环境/生产环境),包括数据源和事务管理器
* @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 {
//关闭配置文件文件流
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}以','分隔的形式转换成{@link Set},如果{@link @value}为null,就用{@link @defaultValue}
*/
protected Set stringSetValueOf(String value, String defaultValue) {
value = value == null ? defaultValue : value;
return new HashSet<>(Arrays.asList(value.split(",")));
}
/**
* 找出对应{@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);
}
}
/**
* 找出对应{@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);
}
}
/**
* 找出对应{@link @alias}的{@link ParameterMode},若{@link @alias}为null返回null,若没有找到就会抛出{@link BuilderException},
* ParameterMode: {@link ParameterMode#IN},{@link ParameterMode#OUT},{@link ParameterMode#INOUT}
*/
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);
}
}
/**
* 创建{@link @alias}的实例,{@link @alias}为null返回null,若在创建抛出任何异常信息都会封装到{@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解析出对应的类 实际调用{@link #resolveAlias(String)},返回类实例
*/
protected 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);
}
}
/**
* 构建TypeHandler,不没有注册到{@link #typeHandlerRegistry},只是返回TypeHandler实例对象,实际调用{@link #resolveTypeHandler(Class, Class)}
* @param javaType 可以不传的,不传就用typeHandlerAlias的无参构造方法,传就尝试调用带有接收javaType的构造方法[p:即使调用失败也不会抛出异常,而是调用无参构造方法]
* @param typeHandlerAlias 若null,该方法直接返回null;若传入的包+类没有继承{@link TypeHandler}会抛出{@link BuilderException}
*/
protected TypeHandler> resolveTypeHandler(Class> javaType, String typeHandlerAlias) {
if (typeHandlerAlias == null) {
return null;
}
//获取typeHandlerAlias对应的类
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 已经严重type是TypeHandler类。
Class extends TypeHandler>> typeHandlerType = (Class extends TypeHandler>>) type;
return resolveTypeHandler(javaType, typeHandlerType);
}
/**
* 构建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找出对应的类
*/
protected 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运行时所用的数据库环境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 加载哪种环境(开发环境/生产环境),包括数据源和事务管理器
* @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对应的XML解析器
* @param environment 加载哪种环境(开发环境/生产环境),包括数据源和事务管理器
* @param props 属性配置文件,那些属性可以用${propName}语法形式多次用在配置文件中
*/
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//新建一个Mybatis全局配置信息类对象
super(new Configuration());
//设置错误报文实例的资源引用
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);//设置属性文件的变量到配置类configuration
this.parsed = false;//parse表示是否已经解析了
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;
//对应节点
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
/**
* 解析mybatis-config.xml所有标签信息,并实例化标签对应的对象,配置进 {@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);
}
}
/**
* 读取 便签信息,每个 的name属性对应着Configuration的属性变量名,
* 该放会检验 标签的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))) {//这里就是判断有没有对应的属性在configuration里,没有就抛出异常
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
/**
* 加载虚拟文件系统配置,读取服务器资源
* VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源
* Mybatis中提供了VFS这个配置,主要是通过该配置可以加载自定义的虚拟文件系统应用程序
*/
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);
}
}
}
}
/**
* 制定日志的实现(log4j等)
*/
private void loadCustomLogImpl(Properties props) {
Class extends Log> logImpl = resolveClass(props.getProperty("logImpl"));//找出logImpl对应的日志类
configuration.setLogImpl(logImpl);
}
/**
* 类型别名元素
* @param parent
*/
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);//通过类加载器加载对应的来
//注册别名到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);
}
}
}
}
}
/**
* 元素,每个插件就是mybatis的拦截器。
* @param parent 标签
*/
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();//加载对应的拦截类,并new一个实例
interceptorInstance.setProperties(properties);//加上 标签内的标签信息
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();//加载对应的ObjectFactory类,并new一个实例
factory.setProperties(properties);//加上 标签内的标签信息
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();//加载对应的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();//加载对应的ReflectorFactory类,并new一个实例
configuration.setReflectorFactory(factory);//注册到ReflectorFactory
}
}
/**
* 读取标签的属性变量,以及合并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属性在标签中只能指定一个
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
//加载对应路径的属性
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);//设置解析器的属性变量
configuration.setVariables(defaults);//设置Configuration的属性变量
}
}
/**
* 将 标签的设置全部设置进去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/>标签元素
*
* 获取<Environments/>下的default属性取得当前Mybatis所需要的环境ID并赋值给 {@link XMLConfigBuilder#environment},
* 然后获取<environment/>的id属性(环境ID)并赋值给{@link @id},判断{@link @id}是不是{@link XMLConfigBuilder#environment},
* 是就做以下操作
*
* - 获取环境ID
* - 获取事务管理器工厂的实例对象,赋值给 {@link @txFactory}
* - 获取数据库数据源工厂的实例对象{@link @dsFactory},然后从{@link @dsFactory} 获取数据源,赋值给 {@link @dataSoure}
* - 传入{@link @txFactory},{@link @dataSoure},{@link @id} 构建 {@link Environment} 传入 {@link XMLConfigBuilder#configuration}
*
*
* @param context
* @throws Exception
*/
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");//默认环境ID,指 的ID属性
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");//环境ID
//只有符合指定的默认环境ID的环境ID才会被解析
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//事务管理器工厂
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//数据源工厂
DataSource dataSource = dsFactory.getDataSource();//取出数据源
//构建环境
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
/**
* <databaseIdProvider/> 解析。【数据库厂商标识】
*
* MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
* MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。
* 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。
* 为支持多厂商特性设置 databaseIdProvider
*
*
* Mybatis内置了一个 {@link org.apache.ibatis.mapping.VendorDatabaseIdProvider},对type属性设置别名'DB_VENDOR'即可使用。
*
* 参考文档
*
* - https://www.cnblogs.com/hellowhy/p/9676037.html
* - http://www.mybatis.org/mybatis-3/zh/configuration.html#databaseIdProvider
*
*/
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中获取数据源,交给databaseIdProvider获取数据库ID。
* 数据ID,其实就是数据库名,或者properties对应key的value(这个需要用户去配置)
*/
Environment environment = configuration.getEnvironment();
if (environment != null && databaseIdProvider != null) {
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
configuration.setDatabaseId(databaseId);
}
}
/**
* 下的 标签信息,返回事务工厂 {@link TransactionFactory}
*
* - type= 'JDBC' ,返回 {@link org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory}
* - type= 'MANAGED' ,返回 {@link org.apache.ibatis.transaction.managed.ManagedTransactionFactory}
* - type='包+类名',返回对应类实例 {@link TransactionFactory} ,该类必须保证继承了 {@link TransactionFactory} 接口
*
* @throws BuilderException 没有设置 就会抛出异常
*/
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.");
}
/**
* 下的 标签信息,返回数据源工厂 {@link DataSourceFactory}
*
* - type= 'JNDI' ,返回 {@link org.apache.ibatis.datasource.jndi.JndiDataSourceFactory}
* - type= 'POOLED' ,返回 {@link org.apache.ibatis.datasource.pooled.PooledDataSourceFactory}
* - type= 'UNPOOLED' ,返回 {@link org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory}
* - type='包+类名',返回对应类实例 {@link DataSourceFactory} ,该类必须保证继承了 {@link DataSourceFactory} 接口
*
* @throws BuilderException 没有设置 就会抛出异常
*/
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/>解释
*
* 调用{#typeHandlerRegistry}注册TypeHandler。就算javaType,jdbcType都是null,都能注册得到TypeHandler,只不过javaType,jdbcType都是null
* 。但是我不建议这样做、
*
* @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 {
/**
* 没有得到javaTypeClass时,连jdbcType都不会判断,因为typeHandlerRegistery已经进行处理了:
* 1、从typeHandlerClass中尝试获取MappedTypes注解的配置javaType,然后如果拿不到,就看看有没有继承TypeReference,从
* TypeReference的泛型T中拿到声明的类型作为javaType,还是拿不到就用null
* 2、从typeHandlerClass中尝试获取MappedJdbcTypes注解信息的jdbcType,拿不到就用null
* 就是说就算拿不到javaType,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");
//使用映射器接口实现类的完全限定类名
String mapperClass = child.getStringAttribute("class");
//如果配置了resource 但是 url以及mapperClass没有配置
if (resource != null && url == null && mapperClass == null) {
//设置错误报文实例的资源引用为resource
ErrorContext.instance().resource(resource);
//获取resource文件输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
//新建一个XML映射文件构建器
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) {
//设置错误报文实例的资源引用为url
ErrorContext.instance().resource(url);
//获取url文件输入流
InputStream inputStream = Resources.getUrlAsStream(url);
//新建一个XML映射文件构建器
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对应的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.");
}
}
}
}
}
/**
* 是否是指定的环境
*
* 通过 {@link XMLConfigBuilder#environment} 是否等于 {@link @id},只有{@link XMLConfigBuilder#environment}
* 或者{@link @id}其中一个为null,都会抛出 {@link BuilderException}
*
* @param id 环境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 evalNodes(String expression) {
return evalNodes(document, expression);
}
public List evalNodes(Object root, String expression) {
List 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转换成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 文档时,必须删除元素内容中的空格
factory.setCoalescing(false);//若true,解析器将把 CDATA 节点转换为 Text 节点,并将其附加到相邻(如果有)的 Text 节点
factory.setExpandEntityReferences(true);//若true,的解析器将扩展实体引用节点
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);//设置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);
}
}
/**
* 通用的构造方法
* 使用外部传入的参数,初始化成员变量;
* 构造XPathFactory对象,获得Xpath的对象
* @param validation 设置解析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();
}
}