从本文开始,我们来分享 MyBatis 初始化的流程。在 《精尽 MyBatis 源码分析 —— 项目结构一览》 中,我们简单介绍这个流程如下:
在 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
|
<1>
处,创建 XMLConfigBuilder 对象。<2>
处,调用XMLConfigBuilder#parse()
方法,执行 XML 解析,返回 Configuration 对象。<3>
处,创建 DefaultSqlSessionFactory 对象。- 本文的重点是
<1>
和<2>
处,即 XMLConfigBuilder 类。详细解析,见 「3. XMLConfigBuilder」 。
2. BaseBuilder
org.apache.ibatis.builder.BaseBuilder
,基础构造器抽象类,为子类提供通用的工具类。
为什么不直接讲 XMLConfigBuilder ,而是先讲 BaseBuilder 呢?因为,BaseBuilder 是 XMLConfigBuilder 的父类,并且它还有其他的子类。如下图所示:
2.1 构造方法
// BaseBuilder.java
|
configuration
属性,MyBatis Configuration 对象。XML 和注解中解析到的配置,最终都会设置到org.apache.ibatis.session.Configuration
中。感兴趣的胖友,可以先点击瞅一眼。抽完之后,马上回来。
2.2 parseExpression
#parseExpression(String regex, String defaultValue)
方法,创建正则表达式。代码如下:
// BaseBuilder.java
|
2.3 xxxValueOf
#xxxValueOf(...)
方法,将字符串转换成对应的数据类型的值。代码如下:
// BaseBuilder.java
|
2.4 resolveJdbcType
#resolveJdbcType(String alias)
方法,解析对应的 JdbcType 类型。代码如下:
// BaseBuilder.java
|
2.5 resolveResultSetType
#resolveResultSetType(String alias)
方法,解析对应的 ResultSetType 类型。代码如下:
// BaseBuilder.java
|
2.6 resolveParameterMode
#resolveParameterMode(String alias)
方法,解析对应的 ParameterMode 类型。代码如下:
// BaseBuilder.java
|
2.7 createInstance
#createInstance(String alias)
方法,创建指定对象。代码如下:
// BaseBuilder.java
|
-
<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
|
3. XMLConfigBuilder
org.apache.ibatis.builder.xml.XMLConfigBuilder
,继承 BaseBuilder 抽象类,XML 配置构建器,主要负责解析 mybatis-config.xml 配置文件。即对应 《MyBatis 文档 —— XML 映射配置文件》 。
3.1 构造方法
// XMLConfigBuilder.java
|
parser
属性,XPathParser 对象。在 《精尽 MyBatis 源码分析 —— 解析器模块》 中,已经详细解析。localReflectorFactory
属性,DefaultReflectorFactory 对象。在 《精尽 MyBatis 源码分析 —— 反射模块》 中,已经详细解析。-
构造方法重载了比较多,只需要看最后一个。
<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
|
<1.1>
处,若已解析,抛出 BuilderException 异常。<1.2>
处,标记已解析。<2>
处,调用XPathParser#evalNode(String expression)
方法,获得 XML<configuration />
节点,后调用#parseConfiguration(XNode root)
方法,解析该节点。详细解析,见 「3.3 parseConfiguration」 。
3.3 parseConfiguration
#parseConfiguration(XNode root)
方法,解析 <configuration />
节点。代码如下:
// XMLConfigBuilder.java
|
<1>
处,调用#propertiesElement(XNode context)
方法,解析<properties />
节点。详细解析,见 「3.3.1 propertiesElement」 。<2>
处,调用#settingsAsProperties(XNode context)
方法,解析<settings />
节点。详细解析,见 「3.3.2 settingsAsProperties」 。<3>
处,调用#loadCustomVfs(Properties settings)
方法,加载自定义 VFS 实现类。详细解析,见 「3.3.3 loadCustomVfs」 。<4>
处,调用#typeAliasesElement(XNode parent)
方法,解析<typeAliases />
节点。详细解析,见 「3.3.4 typeAliasesElement」 。<5>
处,调用#typeAliasesElement(XNode parent)
方法,解析<plugins />
节点。详细解析,见 「3.3.5 pluginElement」 。<6>
处,调用#objectFactoryElement(XNode parent)
方法,解析<objectFactory />
节点。详细解析,见 「3.3.6 pluginElement」 。<7>
处,调用#objectWrapperFactoryElement(XNode parent)
方法,解析<objectWrapperFactory />
节点。详细解析,见 「3.3.7 objectWrapperFactoryElement」 。<8>
处,调用#reflectorFactoryElement(XNode parent)
方法,解析<reflectorFactory />
节点。详细解析,见 「3.3.8 reflectorFactoryElement」 。<9>
处,调用#settingsElement(Properties props)
方法,赋值<settings />
到 Configuration 属性。详细解析,见 「3.3.9 settingsElement」 。<10>
处,调用#environmentsElement(XNode context)
方法,解析<environments />
标签。详细解析,见 「3.3.10 environmentsElement」 。<11>
处,调用#databaseIdProviderElement(XNode context)
方法,解析<databaseIdProvider />
标签。详细解析,见 「3.3.11 databaseIdProviderElement」 。<12>
处,调用#typeHandlerElement(XNode context)
方法,解析<typeHandlers />
标签。详细解析,见 「3.3.12 typeHandlerElement」 。<13>
处,调用#mapperElement(XNode context)
方法,解析<mappers />
标签。详细解析,见 「3.3.13 mapperElement」 。
3.3.1 propertiesElement
#propertiesElement(XNode context)
方法,解析 <properties />
节点。大体逻辑如下:
- 解析
<properties />
标签,成 Properties 对象。 - 覆盖
configuration
中的 Properties 对象到上面的结果。 - 设置结果到
parser
和configuration
中。
代码如下:
// XMLConfigBuilder.java
|
3.3.2 settingsAsProperties
#settingsElement(Properties props)
方法,将 <setting />
标签解析为 Properties 对象。代码如下:
// XMLConfigBuilder.java
|
3.3.3 loadCustomVfs
#loadCustomVfs(Properties settings)
方法,加载自定义 VFS 实现类。代码如下:
// XMLConfigBuilder.java
|
3.3.4 typeAliasesElement
#typeAliasesElement(XNode parent)
方法,解析 <typeAliases />
标签,将配置类注册到 typeAliasRegistry
中。代码如下:
// XMLConfigBuilder.java
|
3.3.5 pluginElement
#pluginElement(XNode parent)
方法,解析 <plugins />
标签,添加到 Configuration#interceptorChain
中。代码如下:
// XMLConfigBuilder.java
|
<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
|
<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
|
<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
|
<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
|
- 属性比较多,瞟一眼就行。
3.3.10 environmentsElement
#environmentsElement(XNode context)
方法,解析 <environments />
标签。代码如下:
// XMLConfigBuilder.java
|
<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
|
3.3.11 databaseIdProviderElement
#databaseIdProviderElement(XNode context)
方法,解析 <databaseIdProvider />
标签。代码如下:
// XMLConfigBuilder.java
|
- 不了解的胖友,可以先看看 《MyBatis 文档 —— XML 映射配置文件 —— 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 {
|
3.3.11.2 VendorDatabaseIdProvider
org.apache.ibatis.mapping.VendorDatabaseIdProvider
,实现 DatabaseIdProvider 接口,供应商数据库标识提供器实现类。
① 构造方法
// VendorDatabaseIdProvider.java
|
② 获得数据库标识
#getDatabaseId(DataSource dataSource)
方法,代码如下:
// VendorDatabaseIdProvider.java
|
-
<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
|
- 遍历子节点,分别处理
<1>
是<package />
和<2>
是<typeHandler />
两种标签的情况。逻辑比较简单,最终都是注册到typeHandlerRegistry
中。
3.3.13 mapperElement
#mapperElement(XNode context)
方法,解析 <mappers />
标签。代码如下:
// XMLConfigBuilder.java
|
<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>
是类似的