mybatis初始化加载mybatis-config.xml文件
从本文开始,我们来分享 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>是类似的

浙公网安备 33010602011771号