code-learning/mybatis/15-mybatis-MyBatis 初始化(一)之加载 mybatis-config.md

1293 lines
49 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 精尽 MyBatis 源码分析 —— MyBatis 初始化(一)之加载 mybatis-config
# 1. 概述
从本文开始,我们来分享 MyBatis 初始化的流程。在 [《精尽 MyBatis 源码分析 —— 项目结构一览》](http://svip.iocoder.cn/MyBatis/intro) 中,我们简单介绍这个流程如下:
> 在 MyBatis 初始化过程中,会加载 `mybatis-config.xml` 配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 Configuration 对象中。例如:
>
> - `<resultMap>`节点(即 ResultSet 的映射规则) 会被解析成 ResultMap 对象。
> - `<result>` 节点(即属性映射)会被解析成 ResultMapping 对象。
>
> 之后,利用该 Configuration 对象创建 SqlSessionFactory对象。待 MyBatis 初始化之后,开发人员可以通过初始化得到 SqlSessionFactory 创建 SqlSession 对象并完成数据库操作。
- 对应 `builder` 模块,为配置**解析过程**
- 对应 `mapping` 模块,主要为 SQL 操作解析后的**映射**。
因为整个 MyBatis 的初始化流程涉及代码颇多,所以拆分成三篇文章:
- 加载 `mybatis-config.xml` 配置文件。
- 加载 Mapper 映射配置文件。
- 加载 Mapper 接口中的注解信息。
本文就主要分享第一部分「加载 `mybatis-config.xml` 配置文件」。
------
MyBatis 的初始化流程的**入口**是 SqlSessionFactoryBuilder 的 `#build(Reader reader, String environment, Properties properties)` 方法,代码如下:
> SqlSessionFactoryBuilder 中build 方法有多种重载方式。这里就选取一个。
```
// SqlSessionFactoryBuilder.java
/**
* 构造 SqlSessionFactory 对象
*
* @param reader Reader 对象
* @param environment 环境
* @param properties Properties 变量
* @return SqlSessionFactory 对象
*/
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// <1> 创建 XMLConfigBuilder 对象
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// <2> 执行 XML 解析
// <3> 创建 DefaultSqlSessionFactory 对象
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.
}
}
}
```
- `<1>` 处,创建 XMLConfigBuilder 对象。
- `<2>` 处,调用 `XMLConfigBuilder#parse()` 方法,执行 XML 解析,返回 Configuration 对象。
- `<3>` 处,创建 DefaultSqlSessionFactory 对象。
- 本文的重点是 `<1>``<2>` 处,即 XMLConfigBuilder 类。详细解析,见 [「3. XMLConfigBuilder」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
# 2. BaseBuilder
`org.apache.ibatis.builder.BaseBuilder` ,基础构造器抽象类,为子类提供通用的工具类。
为什么不直接讲 XMLConfigBuilder ,而是先讲 BaseBuilder 呢因为BaseBuilder 是 XMLConfigBuilder 的父类,并且它还有其他的子类。如下图所示:[![BaseBuilder 类图](15-mybatis-MyBatis 初始化(一)之加载 mybatis-config.assets/01.png)](http://static.iocoder.cn/images/MyBatis/2020_02_10/01.png)BaseBuilder 类图
## 2.1 构造方法
```
// BaseBuilder.java
/**
* MyBatis Configuration 对象
*/
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
```
- `configuration` 属性MyBatis Configuration 对象。XML 和注解中解析到的配置,最终都会设置到 [`org.apache.ibatis.session.Configuration`](https://github.com/YunaiV/mybatis-3/blob/master/src/main/java/org/apache/ibatis/session/Configuration.java) 中。感兴趣的胖友,可以先点击瞅一眼。抽完之后,马上回来。
## 2.2 parseExpression
`#parseExpression(String regex, String defaultValue)` 方法,创建正则表达式。代码如下:
```
// BaseBuilder.java
/**
* 创建正则表达式
*
* @param regex 指定表达式
* @param defaultValue 默认表达式
* @return 正则表达式
*/
@SuppressWarnings("SameParameterValue")
protected Pattern parseExpression(String regex, String defaultValue) {
return Pattern.compile(regex == null ? defaultValue : regex);
}
```
## 2.3 xxxValueOf
`#xxxValueOf(...)` 方法,将字符串转换成对应的数据类型的值。代码如下:
```
// BaseBuilder.java
protected Boolean booleanValueOf(String value, Boolean defaultValue) {
return value == null ? defaultValue : Boolean.valueOf(value);
}
protected Integer integerValueOf(String value, Integer defaultValue) {
return value == null ? defaultValue : Integer.valueOf(value);
}
protected Set<String> stringSetValueOf(String value, String defaultValue) {
value = (value == null ? defaultValue : value);
return new HashSet<>(Arrays.asList(value.split(",")));
}
```
## 2.4 resolveJdbcType
`#resolveJdbcType(String alias)` 方法,解析对应的 JdbcType 类型。代码如下:
```
// BaseBuilder.java
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);
}
}
```
## 2.5 resolveResultSetType
`#resolveResultSetType(String alias)` 方法,解析对应的 ResultSetType 类型。代码如下:
```
// BaseBuilder.java
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);
}
}
```
## 2.6 resolveParameterMode
`#resolveParameterMode(String alias)` 方法,解析对应的 ParameterMode 类型。代码如下:
```
// BaseBuilder.java
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);
}
}
```
## 2.7 createInstance
`#createInstance(String alias)` 方法,创建指定对象。代码如下:
```
// BaseBuilder.java
protected Object createInstance(String alias) {
// <1> 获得对应的类型
Class<?> clazz = resolveClass(alias);
if (clazz == null) {
return null;
}
try {
// <2> 创建对象
return resolveClass(alias).newInstance(); // 这里重复获得了一次
} catch (Exception e) {
throw new BuilderException("Error creating instance. Cause: " + e, e);
}
}
```
- `<1>` 处,调用 `#resolveClass(String alias)` 方法,获得对应的类型。代码如下:
```
// BaseBuilder.java
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);
}
}
protected <T> Class<? extends T> resolveAlias(String alias) {
return typeAliasRegistry.resolveAlias(alias);
}
```
- 从 `typeAliasRegistry` 中,通过别名或类全名,获得对应的类。
- `<2>` 处,创建对象。
## 2.8 resolveTypeHandler
`#resolveTypeHandler(Class<?> javaType, String typeHandlerAlias)` 方法,从 `typeHandlerRegistry` 中获得或创建对应的 TypeHandler 对象。代码如下:
```
// BaseBuilder.java
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
// 先获得 TypeHandler 对象
TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
if (handler == null) { // 如果不存在,进行创建 TypeHandler 对象
// not in registry, create a new one
handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
}
return handler;
}
```
# 3. XMLConfigBuilder
`org.apache.ibatis.builder.xml.XMLConfigBuilder` ,继承 BaseBuilder 抽象类XML 配置构建器,主要负责解析 mybatis-config.xml 配置文件。即对应 [《MyBatis 文档 —— XML 映射配置文件》](http://www.mybatis.org/mybatis-3/zh/configuration.html) 。
## 3.1 构造方法
```
// XMLConfigBuilder.java
/**
* 是否已解析
*/
private boolean parsed;
/**
* 基于 Java XPath 解析器
*/
private final XPathParser parser;
/**
* 环境
*/
private String environment;
/**
* ReflectorFactory 对象
*/
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);
}
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// <1> 创建 Configuration 对象
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
// <2> 设置 Configuration 的 variables 属性
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
```
- `parser` 属性XPathParser 对象。在 [《精尽 MyBatis 源码分析 —— 解析器模块》](http://svip.iocoder.cn/MyBatis/parsing-package) 中,已经详细解析。
- `localReflectorFactory` 属性DefaultReflectorFactory 对象。在 [《精尽 MyBatis 源码分析 —— 反射模块》](http://svip.iocoder.cn/MyBatis/reflection-package) 中,已经详细解析。
- 构造方法重载了比较多,只需要看最后一个。
- `<1>` 处,创建 Configuration 对象。
- `<2>` 处,设置 Configuration 对象的 `variables` 属性。代码如下:
```
// Configuration.java
/**
* 变量 Properties 对象。
*
* 参见 {@link org.apache.ibatis.builder.xml.XMLConfigBuilder#propertiesElement(XNode context)} 方法
*/
protected Properties variables = new Properties();
public void setVariables(Properties variables) {
this.variables = variables;
}
```
## 3.2 parse
`#parse()` 方法,解析 XML 成 Configuration 对象。代码如下:
```
// XMLConfigBuilder.java
public Configuration parse() {
// <1.1> 若已解析,抛出 BuilderException 异常
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
// <1.2> 标记已解析
parsed = true;
// <2> 解析 XML configuration 节点
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
```
- `<1.1>` 处,若已解析,抛出 BuilderException 异常。
- `<1.2>` 处,标记已解析。
- `<2>` 处,调用 `XPathParser#evalNode(String expression)` 方法,获得 XML `<configuration />` 节点,后调用 `#parseConfiguration(XNode root)` 方法,解析该节点。详细解析,见 [「3.3 parseConfiguration」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
## 3.3 parseConfiguration
`#parseConfiguration(XNode root)` 方法,解析 `<configuration />` 节点。代码如下:
```
// XMLConfigBuilder.java
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// <1> 解析 <properties /> 标签
propertiesElement(root.evalNode("properties"));
// <2> 解析 <settings /> 标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
// <3> 加载自定义 VFS 实现类
loadCustomVfs(settings);
// <4> 解析 <typeAliases /> 标签
typeAliasesElement(root.evalNode("typeAliases"));
// <5> 解析 <plugins /> 标签
pluginElement(root.evalNode("plugins"));
// <6> 解析 <objectFactory /> 标签
objectFactoryElement(root.evalNode("objectFactory"));
// <7> 解析 <objectWrapperFactory /> 标签
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// <8> 解析 <reflectorFactory /> 标签
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// <9> 赋值 <settings /> 到 Configuration 属性
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// <10> 解析 <environments /> 标签
environmentsElement(root.evalNode("environments"));
// <11> 解析 <databaseIdProvider /> 标签
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// <12> 解析 <typeHandlers /> 标签
typeHandlerElement(root.evalNode("typeHandlers"));
// <13> 解析 <mappers /> 标签
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
```
- `<1>` 处,调用 `#propertiesElement(XNode context)` 方法,解析 `<properties />` 节点。详细解析,见 [「3.3.1 propertiesElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<2>` 处,调用 `#settingsAsProperties(XNode context)` 方法,解析 `<settings />` 节点。详细解析,见 [「3.3.2 settingsAsProperties」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<3>` 处,调用 `#loadCustomVfs(Properties settings)` 方法,加载自定义 VFS 实现类。详细解析,见 [「3.3.3 loadCustomVfs」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<4>` 处,调用 `#typeAliasesElement(XNode parent)` 方法,解析 `<typeAliases />` 节点。详细解析,见 [「3.3.4 typeAliasesElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<5>` 处,调用 `#typeAliasesElement(XNode parent)` 方法,解析 `<plugins />` 节点。详细解析,见 [「3.3.5 pluginElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<6>` 处,调用 `#objectFactoryElement(XNode parent)` 方法,解析 `<objectFactory />` 节点。详细解析,见 [「3.3.6 pluginElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<7>` 处,调用 `#objectWrapperFactoryElement(XNode parent)` 方法,解析 `<objectWrapperFactory />` 节点。详细解析,见 [「3.3.7 objectWrapperFactoryElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<8>` 处,调用 `#reflectorFactoryElement(XNode parent)` 方法,解析 `<reflectorFactory />` 节点。详细解析,见 [「3.3.8 reflectorFactoryElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<9>` 处,调用 `#settingsElement(Properties props)` 方法,赋值 `<settings />` 到 Configuration 属性。详细解析,见 [「3.3.9 settingsElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<10>` 处,调用 `#environmentsElement(XNode context)` 方法,解析 `<environments />` 标签。详细解析,见 [「3.3.10 environmentsElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<11>` 处,调用 `#databaseIdProviderElement(XNode context)` 方法,解析 `<databaseIdProvider />` 标签。详细解析,见 [「3.3.11 databaseIdProviderElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<12>` 处,调用 `#typeHandlerElement(XNode context)` 方法,解析 `<typeHandlers />` 标签。详细解析,见 [「3.3.12 typeHandlerElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
- `<13>` 处,调用 `#mapperElement(XNode context)` 方法,解析 `<mappers />` 标签。详细解析,见 [「3.3.13 mapperElement」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 。
### 3.3.1 propertiesElement
`#propertiesElement(XNode context)` 方法,解析 `<properties />` 节点。大体逻辑如下:
1. 解析 `<properties />` 标签,成 Properties 对象。
2. 覆盖 `configuration` 中的 Properties 对象到上面的结果。
3. 设置结果到 `parser` 和 `configuration` 中。
代码如下:
```
// XMLConfigBuilder.java
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 读取子标签们,为 Properties 对象
Properties defaults = context.getChildrenAsProperties();
// 读取 resource 和 url 属性
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) { // resource 和 url 都存在的情况下,抛出 BuilderException 异常
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
// 读取本地 Properties 配置文件到 defaults 中。
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
// 读取远程 Properties 配置文件到 defaults 中。
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 覆盖 configuration 中的 Properties 对象到 defaults 中。
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
// 设置 defaults 到 parser 和 configuration 中。
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
```
### 3.3.2 settingsAsProperties
`#settingsElement(Properties props)` 方法,将 `<setting />` 标签解析为 Properties 对象。代码如下:
```
// XMLConfigBuilder.java
private Properties settingsAsProperties(XNode context) {
// 将子标签,解析成 Properties 对象
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
// 校验每个属性,在 Configuration 中,有相应的 setting 方法,否则抛出 BuilderException 异常
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;
}
```
### 3.3.3 loadCustomVfs
`#loadCustomVfs(Properties settings)` 方法,加载自定义 VFS 实现类。代码如下:
```
// XMLConfigBuilder.java
private void loadCustomVfs(Properties props) throws ClassNotFoundException {
// 获得 vfsImpl 属性
String value = props.getProperty("vfsImpl");
if (value != null) {
// 使用 , 作为分隔符,拆成 VFS 类名的数组
String[] clazzes = value.split(",");
// 遍历 VFS 类名的数组
for (String clazz : clazzes) {
if (!clazz.isEmpty()) {
// 获得 VFS 类
@SuppressWarnings("unchecked")
Class<? extends VFS> vfsImpl = (Class<? extends VFS>) Resources.classForName(clazz);
// 设置到 Configuration 中
configuration.setVfsImpl(vfsImpl);
}
}
}
}
// Configuration.java
/**
* VFS 实现类
*/
protected Class<? extends VFS> vfsImpl;
public void setVfsImpl(Class<? extends VFS> vfsImpl) {
if (vfsImpl != null) {
// 设置 vfsImpl 属性
this.vfsImpl = vfsImpl;
// 添加到 VFS 中的自定义 VFS 类的集合
VFS.addImplClass(this.vfsImpl);
}
}
```
### 3.3.4 typeAliasesElement
`#typeAliasesElement(XNode parent)` 方法,解析 `<typeAliases />` 标签,将配置类注册到 `typeAliasRegistry` 中。代码如下:
```
// XMLConfigBuilder.java
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) { // 若类不存在,则抛出 BuilderException 异常
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
```
### 3.3.5 pluginElement
`#pluginElement(XNode parent)` 方法,解析 `<plugins />` 标签,添加到 `Configuration#interceptorChain` 中。代码如下:
```
// XMLConfigBuilder.java
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
// 遍历 <plugins /> 标签
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
// <1> 创建 Interceptor 对象,并设置属性
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
// <2> 添加到 configuration 中
configuration.addInterceptor(interceptorInstance);
}
}
}
```
- `<1>` 处,创建 Interceptor 对象,并设置属性。关于 Interceptor 类,后续文章,详细解析。
- `<2>` 处,调用 `Configuration#addInterceptor(Interceptor interceptor)` 方法,添加到 `configuration` 中。代码如下:
```
// Configuration.java
/**
* 拦截器链
*/
protected final InterceptorChain interceptorChain = new InterceptorChain();
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
```
- 关于 InterceptorChain 类,后续文章,详细解析。
### 3.3.6 objectFactoryElement
`#objectFactoryElement(XNode parent)` 方法,解析 `<objectFactory />` 节点。代码如下:
```
// XMLConfigBuilder.java
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
// 获得 ObjectFactory 的实现类
String type = context.getStringAttribute("type");
// 获得 Properties 属性
Properties properties = context.getChildrenAsProperties();
// <1> 创建 ObjectFactory 对象,并设置 Properties 属性
ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
factory.setProperties(properties);
// <2> 设置 Configuration 的 objectFactory 属性
configuration.setObjectFactory(factory);
}
}
```
- `<1>` 处,创建 ObjectFactory 对象,并设置 Properties 属性。
- `<2>` 处,调用 `Configuration#setObjectFactory(ObjectFactory objectFactory)` 方法,设置 Configuration 的 `objectFactory` 属性。代码如下:
```
// Configuration.java
/**
* ObjectFactory 对象
*/
protected ObjectFactory objectFactory = new DefaultObjectFactory();
public void setObjectFactory(ObjectFactory objectFactory) {
this.objectFactory = objectFactory;
}
```
### 3.3.7 objectWrapperFactoryElement
`#objectWrapperFactoryElement(XNode context)` 方法,解析 `<objectWrapperFactory />` 节点。代码如下:
```
// XMLConfigBuilder.java
private void objectWrapperFactoryElement(XNode context) throws Exception {
if (context != null) {
// 获得 ObjectFactory 的实现类
String type = context.getStringAttribute("type");
// <1> 创建 ObjectWrapperFactory 对象
ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
// 设置 Configuration 的 objectWrapperFactory 属性
configuration.setObjectWrapperFactory(factory);
}
}
```
- `<1>` 处,创建 ObjectWrapperFactory 对象。
- `<2>` 处,调用 `Configuration#setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory)` 方法,设置 Configuration 的 `objectWrapperFactory` 属性。代码如下:
```
// Configuration.java
/**
* ObjectWrapperFactory 对象
*/
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
public void setObjectWrapperFactory(ObjectWrapperFactory objectWrapperFactory) {
this.objectWrapperFactory = objectWrapperFactory;
}
```
### 3.3.8 reflectorFactoryElement
`#reflectorFactoryElement(XNode parent)` 方法,解析 `<reflectorFactory />` 节点。代码如下:
```
// XMLConfigBuilder.java
private void reflectorFactoryElement(XNode context) throws Exception {
if (context != null) {
// 获得 ReflectorFactory 的实现类
String type = context.getStringAttribute("type");
// 创建 ReflectorFactory 对象
ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
// 设置 Configuration 的 reflectorFactory 属性
configuration.setReflectorFactory(factory);
}
}
```
- `<1>` 处,创建 ReflectorFactory 对象。
- `<2>` 处,调用 `Configuration#setReflectorFactory(ReflectorFactory reflectorFactory)` 方法,设置 Configuration 的 `reflectorFactory` 属性。代码如下:
```
// Configuration.java
/**
* ReflectorFactory 对象
*/
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
public void setReflectorFactory(ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
}
```
### 3.3.9 settingsElement
`#settingsElement(Properties props)` 方法,赋值 `<settings />` 到 Configuration 属性。代码如下:
```
// XMLConfigBuilder.java
private void settingsElement(Properties props) throws Exception {
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")));
@SuppressWarnings("unchecked")
Class<? extends TypeHandler> typeHandler = resolveClass(props.getProperty("defaultEnumTypeHandler"));
configuration.setDefaultEnumTypeHandler(typeHandler);
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"));
@SuppressWarnings("unchecked")
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
configuration.setLogImpl(logImpl);
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
```
- 属性比较多,瞟一眼就行。
### 3.3.10 environmentsElement
`#environmentsElement(XNode context)` 方法,解析 `<environments />` 标签。代码如下:
```
// XMLConfigBuilder.java
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
// <1> environment 属性非空,从 default 属性获得
if (environment == null) {
environment = context.getStringAttribute("default");
}
// 遍历 XNode 节点
for (XNode child : context.getChildren()) {
// <2> 判断 environment 是否匹配
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
// <3> 解析 `<transactionManager />` 标签,返回 TransactionFactory 对象
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// <4> 解析 `<dataSource />` 标签,返回 DataSourceFactory 对象
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
// <5> 创建 Environment.Builder 对象
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// <6> 构造 Environment 对象,并设置到 configuration 中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
```
- `<1>` 处,若 `environment` 属性非空,从 `default` 属性种获得 `environment` 属性。
- `<2>` 处,遍历 XNode 节点,获得其 `id` 属性,后调用 `#isSpecifiedEnvironment(String id)` 方法,判断 `environment` 和 `id` 是否匹配。代码如下:
```
// XMLConfigBuilder.java
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;
}
```
- `<3>` 处,调用 `#transactionManagerElement(XNode context)` 方法,解析 `<transactionManager />` 标签,返回 TransactionFactory 对象。代码如下:
```
// XMLConfigBuilder.java
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
// 获得 TransactionFactory 的类
String type = context.getStringAttribute("type");
// 获得 Properties 属性
Properties props = context.getChildrenAsProperties();
// 创建 TransactionFactory 对象,并设置属性
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
```
- `<4>` 处,调用 `#dataSourceElement(XNode context)` 方法,解析 `<dataSource />` 标签,返回 DataSourceFactory 对象,而后返回 DataSource 对象。代码如下:
```
// XMLConfigBuilder.java
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
// 获得 DataSourceFactory 的类
String type = context.getStringAttribute("type");
// 获得 Properties 属性
Properties props = context.getChildrenAsProperties();
// 创建 DataSourceFactory 对象,并设置属性
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
```
- `<5>` 处,创建 Environment.Builder 对象。
- `<6>` 处,构造 Environment 对象,并设置到 `configuration` 中。代码如下:
```
// Configuration.java
/**
* DB Environment 对象
*/
protected Environment environment;
public void setEnvironment(Environment environment) {
this.environment = environment;
}
```
#### 3.3.10.1 Environment
`org.apache.ibatis.mapping.Environment` DB 环境。代码如下:
```
// Environment.java
public final class Environment {
/**
* 环境变好
*/
private final String id;
/**
* TransactionFactory 对象
*/
private final TransactionFactory transactionFactory;
/**
* DataSource 对象
*/
private final DataSource dataSource;
public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
if (id == null) {
throw new IllegalArgumentException("Parameter 'id' must not be null");
}
if (transactionFactory == null) {
throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
}
this.id = id;
if (dataSource == null) {
throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
}
this.transactionFactory = transactionFactory;
this.dataSource = dataSource;
}
/**
* 构造器
*/
public static class Builder {
/**
* 环境变好
*/
private String id;
/**
* TransactionFactory 对象
*/
private TransactionFactory transactionFactory;
/**
* DataSource 对象
*/
private DataSource dataSource;
public Builder(String id) {
this.id = id;
}
public Builder transactionFactory(TransactionFactory transactionFactory) {
this.transactionFactory = transactionFactory;
return this;
}
public Builder dataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
public String id() {
return this.id;
}
public Environment build() {
return new Environment(this.id, this.transactionFactory, this.dataSource);
}
}
public String getId() {
return this.id;
}
public TransactionFactory getTransactionFactory() {
return this.transactionFactory;
}
public DataSource getDataSource() {
return this.dataSource;
}
}
```
### 3.3.11 databaseIdProviderElement
`#databaseIdProviderElement(XNode context)` 方法,解析 `<databaseIdProvider />` 标签。代码如下:
```
// XMLConfigBuilder.java
private void databaseIdProviderElement(XNode context) throws Exception {
DatabaseIdProvider databaseIdProvider = null;
if (context != null) {
// <1> 获得 DatabaseIdProvider 的类
String type = context.getStringAttribute("type");
// awful patch to keep backward compatibility 保持兼容
if ("VENDOR".equals(type)) {
type = "DB_VENDOR";
}
// <2> 获得 Properties 对象
Properties properties = context.getChildrenAsProperties();
// <3> 创建 DatabaseIdProvider 对象,并设置对应的属性
databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
databaseIdProvider.setProperties(properties);
}
Environment environment = configuration.getEnvironment();
if (environment != null && databaseIdProvider != null) {
// <4> 获得对应的 databaseId 编号
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
// <5> 设置到 configuration 中
configuration.setDatabaseId(databaseId);
}
}
```
- 不了解的胖友,可以先看看 [《MyBatis 文档 —— XML 映射配置文件 —— databaseIdProvider 数据库厂商标识》](http://www.mybatis.org/mybatis-3/zh/configuration.html#databaseIdProvider) 。
- `<1>` 处,获得 DatabaseIdProvider 的类。
- `<2>` 处,获得 Properties 对象。
- `<3>` 处,创建 DatabaseIdProvider 对象,并设置对应的属性。
- `<4>` 处,调用 `DatabaseIdProvider#getDatabaseId(DataSource dataSource)` 方法,获得对应的 `databaseId` **标识**。
- `<5>` 处,设置到 `configuration` 中。代码如下:
```
// Configuration.java
/**
* 数据库标识
*/
protected String databaseId;
public void setDatabaseId(String databaseId) {
this.databaseId = databaseId;
}
```
#### 3.3.11.1 DatabaseIdProvider
`org.apache.ibatis.mapping.DatabaseIdProvider` ,数据库标识提供器接口。代码如下:
```
public interface DatabaseIdProvider {
/**
* 设置属性
*
* @param p Properties 对象
*/
void setProperties(Properties p);
/**
* 获得数据库标识
*
* @param dataSource 数据源
* @return 数据库标识
* @throws SQLException 当 DB 发生异常时
*/
String getDatabaseId(DataSource dataSource) throws SQLException;
}
```
#### 3.3.11.2 VendorDatabaseIdProvider
`org.apache.ibatis.mapping.VendorDatabaseIdProvider` ,实现 DatabaseIdProvider 接口,供应商数据库标识提供器实现类。
**① 构造方法**
```
// VendorDatabaseIdProvider.java
/**
* Properties 对象
*/
private Properties properties;
@Override
public String getDatabaseId(DataSource dataSource) {
if (dataSource == null) {
throw new NullPointerException("dataSource cannot be null");
}
try {
return getDatabaseName(dataSource);
} catch (Exception e) {
log.error("Could not get a databaseId from dataSource", e);
}
return null;
}
@Override
public void setProperties(Properties p) {
this.properties = p;
}
```
**② 获得数据库标识**
`#getDatabaseId(DataSource dataSource)` 方法,代码如下:
```
// VendorDatabaseIdProvider.java
@Override
public String getDatabaseId(DataSource dataSource) {
if (dataSource == null) {
throw new NullPointerException("dataSource cannot be null");
}
try {
// 获得数据库标识
return getDatabaseName(dataSource);
} catch (Exception e) {
log.error("Could not get a databaseId from dataSource", e);
}
return null;
}
private String getDatabaseName(DataSource dataSource) throws SQLException {
// <1> 获得数据库产品名
String productName = getDatabaseProductName(dataSource);
if (this.properties != null) {
for (Map.Entry<Object, Object> property : properties.entrySet()) {
// 如果产品名包含 KEY ,则返回对应的 VALUE
if (productName.contains((String) property.getKey())) {
return (String) property.getValue();
}
}
// no match, return null
return null;
}
// <3> 不存在 properties ,则直接返回 productName
return productName;
}
```
- `<1>` 处,调用 `#getDatabaseProductName(DataSource dataSource)` 方法,获得数据库产品名。代码如下:
```
// VendorDatabaseIdProvider.java
private String getDatabaseProductName(DataSource dataSource) throws SQLException {
try (Connection con = dataSource.getConnection()) {
// 获得数据库连接
DatabaseMetaData metaData = con.getMetaData();
// 获得数据库产品名
return metaData.getDatabaseProductName();
}
}
```
- 通过从 Connection 获得数据库产品名。
- `<2>` 处,如果 `properties` 非空,则从 `properties` 中匹配 `KEY` ?若成功,则返回 `VALUE` ,否则,返回 `null` 。
- `<3>` 处,如果 `properties` 为空,则直接返回 `productName` 。
### 3.3.12 typeHandlerElement
`#typeHandlerElement(XNode parent)` 方法,解析 `<typeHandlers />` 标签。代码如下:
```
// XMLConfigBuilder.java
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
// 遍历子节点
for (XNode child : parent.getChildren()) {
// <1> 如果是 package 标签,则扫描该包
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
// <2> 如果是 typeHandler 标签,则注册该 typeHandler 信息
} else {
// 获得 javaType、jdbcType、handler
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); // 非空
// 注册 typeHandler
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
```
- 遍历子节点,分别处理 `<1>` 是 `<package />` 和 `<2>` 是 `<typeHandler />` 两种标签的情况。逻辑比较简单,最终都是注册到 `typeHandlerRegistry` 中。
### 3.3.13 mapperElement
`#mapperElement(XNode context)` 方法,解析 `<mappers />` 标签。代码如下:
```
// XMLConfigBuilder.java
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// <0> 遍历子节点
for (XNode child : parent.getChildren()) {
// <1> 如果是 package 标签,则扫描该包
if ("package".equals(child.getName())) {
// 获得包名
String mapperPackage = child.getStringAttribute("name");
// 添加到 configuration 中
configuration.addMappers(mapperPackage);
// 如果是 mapper 标签,
} else {
// 获得 resource、url、class 属性
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
// <2> 使用相对于类路径的资源引用
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
// 获得 resource 的 InputStream 对象
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建 XMLMapperBuilder 对象
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 执行解析
mapperParser.parse();
// <3> 使用完全限定资源定位符URL
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
// 获得 url 的 InputStream 对象
InputStream inputStream = Resources.getUrlAsStream(url);
// 创建 XMLMapperBuilder 对象
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// 执行解析
mapperParser.parse();
// <4> 使用映射器接口实现类的完全限定类名
} else if (resource == null && url == null && mapperClass != null) {
// 获得 Mapper 接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 添加到 configuration 中
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
```
- `<0>` 处,遍历子节点,处理每一个节点。根据节点情况,会分成 `<1>`、`<2>`、`<3>`、`<4>` 种情况,并且第一个是处理 `<package />` 标签,后三个是处理 `<mapper />` 标签。
- `<1>` 处,如果是 `<package />` 标签,则获得 `name` 报名,并调用 `Configuration#addMappers(String packageName)` 方法,扫描该包下的所有 Mapper 接口。代码如下:
```
// Configuration.java
/**
* MapperRegistry 对象
*/
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public void addMappers(String packageName) {
// 扫描该包下所有的 Mapper 接口,并添加到 mapperRegistry 中
mapperRegistry.addMappers(packageName);
}
```
- `<4>` 处,如果是 `mapperClass` 非空,则是使用映射器接口实现类的完全限定类名,则获得 Mapper 接口,并调用 `Configuration#addMapper(Class<T> type)` 方法,直接添加到 `configuration` 中。代码如下:
```
// Configuration.java
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
```
- 实际上,`<1>` 和 `<4>` 是相似情况,差别在于前者需要扫描,才能获取到所有的 Mapper 接口,而后者明确知道是哪个 Mapper 接口。
- `<2>` 处,如果是 `resource` 非空,则是使用相对于类路径的资源引用,则需要创建 XMLMapperBuilder 对象,并调用 `XMLMapperBuilder#parse()` 方法,执行解析 Mapper XML 配置。执行之后,我们就能知道这个 Mapper XML 配置对应的 Mapper 接口。关于 XMLMapperBuilder 类,我们放在下一篇博客中,详细解析。
- `<3>` 处,如果是 `url` 非空则是使用完全限定资源定位符URL情况和 `<2>` 是类似的。
# 666. 彩蛋
一篇体力活的文章,有点无趣哈。
参考和推荐如下文章:
- 祖大俊 [《Mybatis3.3.x技术内幕Mybatis初始化流程](https://my.oschina.net/zudajun/blog/668738)
- 田小波 [《MyBatis 源码分析 - 配置文件解析过程》](https://www.tianxiaobo.com/2018/07/20/MyBatis-源码分析-配置文件解析过程/)
- 无忌 [《MyBatis 源码解读之配置》](https://my.oschina.net/wenjinglian/blog/1833051)
- 徐郡明 [《MyBatis 技术内幕》](https://item.jd.com/12125531.html) 的 [「3.1 MyBatis 初始化」](https://svip.iocoder.cn/MyBatis/builder-package-1/#) 小节