案例研究之聊聊 Mybatis 源码 (一)

用户头像
小诚信驿站
关注
发布于: 2020 年 07 月 15 日
案例研究之聊聊Mybatis源码(一)



一直以来想看下Mybatis源码,但是一直没时间看或者说自己本身没有去花时间做这件事情。随着自己的成长,觉得技术这个事情还是需要花时间去搞源码的。所以花时间来看看前辈的优秀的作品,从而也给自己的未来编程提高些能力。

有些事情现在不去做,可能以后也不会去做了,如果一直有想法,那么趁现在就去做吧!

----刘晓成@小诚信驿站



本次源码分为七个阶段(可以按照序列阅读学习,由于篇幅问题只讲第一个阶段和第二个阶段,其余可看下一篇):

  • 第一个阶段是全局核心重点图(本节)

  • 第二个阶段是构建加载初始化源码分析(本节)

  • 第三个阶段是执行查询源码分析(点该链接

  • 第四个阶段是执行更新源码分析(点该链接

  • 第五个阶段是Mybatis源码中的设计模式(点该链接

  • 第六个阶段是Mybatis源码中的借鉴知识点和面试考察点(点该链接



第一个阶段、全局核心重点图

首先需要明确Mybatis在应用程序中的位置





Mybatis源码包的逻辑架构图





Mybatis应用的逻辑架构图



Mybatis核心加载图



Mybatis核心执行图



Mybatis核心加载执行交互图



小结

第一节就主要是我梳理的核心图,希望能够有够大概意识和指导思想的意识,方便之后的源码学习。



第二个阶段、聊聊源码mybatis(构建加载初始化源码分析)



该节内容主要将Mybatis初始化内容,回顾下我们第一节的核心重点图。





举个case:Mybatis初始化的加载过程时序图





3.1、XML的配置解析示例

3.1.1、mybatis-config.xml的使用示例



//1、xml的协议头
<?xml version="1.0" encoding="UTF-8" ?>
//2、mybatis对于xml,Configuration配置文件的约束协议
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
//3、全局配置对象
<configuration>
//3.1.1、数据源配置1:通过属性注入数据源
<properties resource="org/apache/ibatis/databases/blog/blog-derby.properties"/>
//3.1.2、数据源配置2:通过直接配置
//3.2、全局配置对象的属性
<settings>
//是否开启缓存
<setting name="cacheEnabled" value="true"/>
//是否开启懒加载
<setting name="lazyLoadingEnabled" value="false"/>
//是否允许返回多个结果集
<setting name="multipleResultSetsEnabled" value="true"/>
//是否使用驼峰标示属性
<setting name="useColumnLabel" value="true"/>
//是否使用生成主键策略
<setting name="useGeneratedKeys" value="false"/>
//默认的执行器类型
<setting name="defaultExecutorType" value="SIMPLE"/>
//默认的会话超时时间单位s
<setting name="defaultStatementTimeout" value="25"/>
</settings>
//3.3、实体对象别名映射
<typeAliases>
<typeAlias alias="Author" type="org.apache.ibatis.domain.blog.Author"/>
</typeAliases>
//3.3、指定类型处理器
<typeHandlers>
<typeHandler javaType="String" jdbcType="VARCHAR" handler="org.apache.ibatis.builder.CustomStringTypeHandler"/>
</typeHandlers>
//3.4、对象工厂
<objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory">
<property name="objectFactoryProperty" value="100"/>
</objectFactory>
//3.5、配置会话拦截插件
<plugins>
<plugin interceptor="org.apache.ibatis.builder.ExamplePlugin">
<property name="pluginProperty" value="100"/>
</plugin>
</plugins>
//3.6、配置环境变量: MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)
<environments default="development">
//3.6.1、环境配置变量可以通过ID来区分dev-uat-pre-pro
<environment id="development">
//3.6.1.1、事务管理器
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
//3.6.1.2、数据源配置
<dataSource type="UNPOOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
//3.7、mapper接口配置,包含了 SQL 代码和映射定义信息
<mappers>
<mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/>
</mappers>
</configuration>



3.1.2、Mapper.xml的使用示例



//1、xml的定义
<?xml version="1.0" encoding="UTF-8" ?>
//2、mapper协议的约束
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
//3、映射类路径的mapper,实际上是和对应的java类绑定
<mapper namespace="org.apache.ibatis.domain.blog.mappers.AuthorMapper">
//4、mapper接口方法:根据主键ID查询返回结果map的
<parameterMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
<parameter property="id" />
</parameterMap>
//5、mapper接口方法:对于实体对象和数据库字段的映射情况
<resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
//字段列和对象属性的映射
<id column="id" property="id" />
<result property="username" column="username" />
<result property="favouriteSection" column="favourite_section" />
</resultMap>
//6、mapper接口方法:包含嵌套的对象属性字段映射情况
<resultMap id="selectImmutableAuthor" type="org.apache.ibatis.domain.blog.ImmutableAuthor">
//参数和Java类型映射
<constructor>
<idArg column="id" javaType="_int" />
<arg column="username" javaType="string" />
<arg column="favourite_section" javaType="org.apache.ibatis.domain.blog.Section" />
</constructor>
</resultMap>
//7、mapper接口方法:查询所有结果
<select id="selectAllAuthors" resultType="org.apache.ibatis.domain.blog.Author">
select * from author
</select>
//8、mapper接口方法:查询返回set集
<select id="selectAllAuthorsSet" resultType="org.apache.ibatis.domain.blog.Author">
select * from author
</select>
//9、mapper接口方法:查询返回list集合
<select id="selectAllAuthorsLinkedList" resultType="org.apache.ibatis.domain.blog.Author">
select * from author
</select>
//10、mapper接口方法:查询返回数组
<select id="selectAllAuthorsArray" resultType="org.apache.ibatis.domain.blog.Author">
select * from author
</select>
//11、mapper接口方法:查询只返回部分参数
<select id="selectAuthorLinkedHashMap" resultType="java.util.LinkedHashMap">
select id, username from author where id = #{value}
</select>
//12、mapper接口方法:查询绑定外部传入参数
<select id="selectAuthor" parameterMap="selectAuthor" resultMap="selectAuthor">
select id, username, password, email, bio, favourite_section
from author where id = ?
</select>
//13、mapper接口方法:查询指定入参类型的参数
<select id="selectAuthorWithInlineParams" parameterType="int"
resultType="org.apache.ibatis.domain.blog.Author">
select * from author where id = #{id}
</select>
//14、mapper接口方法:插入实体对象映射
<insert id="insertAuthor" parameterType="org.apache.ibatis.domain.blog.Author">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
</insert>
//15、mapper接口方法:更新实体对象映射
<update id="updateAuthor" parameterType="org.apache.ibatis.domain.blog.Author">
update Author
set username=#{username,
javaType=String},
password=#{password},
email=#{email},
bio=#{bio}
where id=#{id}
</update>
//16、mapper接口方法:删除实体根据主键ID
<delete id="deleteAuthor" parameterType="int">
delete from Author where id = #{id}
</delete>
//17、mapper接口方法:有选择更新字段
<update id="updateAuthorIfNecessary" parameterType="org.apache.ibatis.domain.blog.Author">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
//18、mapper接口方法:根据指定的条件进行查询
<select id="selectWithOptions" resultType="org.apache.ibatis.domain.blog.Author"
fetchSize="200" timeout="10" statementType="PREPARED" resultSetType="SCROLL_SENSITIVE" flushCache="false" useCache="false">
select * from author
</select>
</mapper>

3.1.3、代码中XML的配置解析使用示例

3.1.3.1、从 XML 中构建 SqlSessionFactory

根据Mybatis核心设计图其实可以观察到,整个Mybatis的核心在于一个SqlSession的一系列操作:实体对象和SQL的绑定,执行会话,结果集处理。而SqlSession的管理则来自于SqlSessionFactory,SqlSessionFactory是可以通过SqlSessionFactoryBuilder获得构建。如何构建出符合用户的SqlSessionFactory,则依赖于我们上面提到的Configuration 实例(Mybatis-config.xml)。



⚠️:强调下重点:每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的,SqlSessionFactory 的实例是通过SqlSessionFactoryBuilder从Configuration 实例(Mybatis-config.xml)构建出来的。



​ 从 XML 文件中构建 SqlSessionFactory 的实例非常简单,可以使用类路径下的资源文件进行配置。也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类可以通过它来获取资源输入流(可以参考之后的源码明细篇)。

//我们上面3.1.1节提到的配置文件全路径
String resource = "org/mybatis/example/mybatis-config.xml";
//通过Mybatis提供的Resources工具类获取输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
//根据Mybatis提供的SqlSessionFactoryBuilder构建器构建用户定义的Configuration特有的SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);



3.1.3.2、构建我们真正干活的SqlSession



//SqlSessionFactory 中获取 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession()
//执行上面我们3.1.2节提到的配置文件mapper中的方法,因为xml配置好的映射关系所以我们直接是通过sqlSession就可以拿到映射的mapper,怎么拿到的呢?依然是需要看下第二章中我提到的Mybatis核心设计图,通过动态代理的方式从Configuration对象中找到对应的mapper,method,parameter。所以从这里看Configuration就是整个一个仓库拥有所有你需要的东西。
//从会话中获取绑定好的mapper接口信息
AuthorMapperWithAnnotation mapper = sqlSession.getMapper(AuthorMapperWithAnnotation.class);
//执行mapper接口的实现方法,而这段具体分析就到第四章和第五章可以学习源码,第三章主要将全局初始化这个过程。
Author author = mapper.selectAuthorInfoById(101);



3.2、注解的配置解析示例



3.2.1、Mybatis-config.xml的使用示例



当然这里也可以使用properties文件属性等内容,或者springboot的yml文件等配置。

//1、xml的协议约束
<?xml version="1.0" encoding="UTF-8" ?>
//2、Configuration的协议约束
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
//3、全局配置类的约束
<configuration>
//3.1、全局配置的属性和事务管理器
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value=""/>
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:mem:automapping"/>
<property name="username" value="sa"/>
</dataSource>
</environment>
</environments>
</configuration>



3.2.2、Mapper接口的使用示例



根据我们接口常用的CRUD操作这里分别提供了注解的实现方法形式。

public interface AuthorMapperWithAnnotation {
//使用select注解直接查询
@Select("select id, username, password, email, bio, favourite_section from author where id = #{id}")
Author selectAuthorInfoById(int id);
//使用SelectProvider注解直接查询
@SelectProvider(type =AuthorProvider.class,method = "selectAuthorWithPlaceholder")
Author selectAuthorWithPlaceholder(Author author);
//使用Insert注解直接查询
@Insert("insert into author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio})")
int insertAuthor(Author author);
//使用InsertProvider注解直接查询
@InsertProvider(type =AuthorProvider.class,method = "insertAuthorWithPlaceholder")
int insertAuthorWithPlaceholder(Author author);
//使用Update注解直接查询
@Update("update author set username=#{username} where id =#{id}")
int updateAuthor(Author author);
//使用UpdateProvider注解直接查询
@UpdateProvider(type =AuthorProvider.class,method = "updateAuthorWithPlaceholder")
void updateAuthorWithPlaceholder(Author author);
//使用Delete注解直接查询
@Delete("delete from author where id = #{id}")
int deleteAuthor(Author author);
//使用DeleteProvider注解直接查询
@DeleteProvider(type =AuthorProvider.class,method = "deleteAuthorWithPlaceholder")
int deleteAuthorWithPlaceholder(Author author);
//提供provider注解类的对应方法
class AuthorProvider{
//selectAuthorWithPlaceholder方法
public String selectAuthorWithPlaceholder(Author author){
return new SQL(){{
SELECT("id,username,password,email,bio").FROM("author").WHERE("id=#{id}");
}}.toString();
}
//insertAuthorWithPlaceholder方法
public String insertAuthorWithPlaceholder(Author author){
return new SQL(){{
INSERT_INTO("Author").VALUES("id,username,password,email,bio","#{id},#{username},#{password},#{email},#{bio}");
}}.toString();
}
//updateAuthorWithPlaceholder方法
public String updateAuthorWithPlaceholder(Author author){
return new SQL(){{
UPDATE("Author").SET("id=#{id}","username=#{username}","password=#{password}","email=#{email}","bio=#{bio}").WHERE("id=#{id}");
}}.toString();
}
//deleteAuthorWithPlaceholder方法
public String deleteAuthorWithPlaceholder(Author author){
return new SQL(){{
DELETE_FROM("Author").WHERE("id=#{id}");
}}.toString();
}
}
}



3.3.3、代码中注解的解析使用示例



//上述提到的输入流信息我们这里先以xml为例子
final String resource = "org/apache/ibatis/builder/Mybatis-config.xml";
final Reader reader = Resources.getResourceAsReader(resource);
//直接代码构建SqlSessionFactory
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
//初始化全局配置---相当于Mybatis-config.xml中的绑定configuration标签
Configuration configuration = new Configuration(sqlMapper.getConfiguration().getEnvironment());
//为全局配置添加需要的Mapper接口---相当于Mybatis-config.xml中的绑定Mapper.xml文件的标签
configuration.addMapper(AuthorMapperWithAnnotation.class);
//创建SQL会话工厂---利用SqlSessionFactory实现Bean实例化
SqlSessionFactory sqlMapperWithAnnotation = new DefaultSqlSessionFactory(configuration);
//获取SQL会话链接---所有Mybatis的核心API接口
SqlSession sqlSession = sqlMapperWithAnnotation.openSession();
//从会话中获取绑定好的mapper接口信息---利用反射获取对应的mapper
AuthorMapperWithAnnotation mapper = sqlSession.getMapper(AuthorMapperWithAnnotation.class);
//执行插入会话---Mybatis核心设计图中动态代理执行过程(这里的源码分析可以参考第四章、第五章)
Author expected = new Author(500, "wolf:\"test\"", "******", "liuxiaocheng@somewhere.com", "Something...", null);
mapper.insertAuthorWithPlaceholder(expected);

3.3、核心类和核心方法

SqlSessionFactoryBuilder:SqlSessionFactory的构造器,可以自己解析配置,也可以通过提前构建好的配置对象构建。

SqlSessionFactory:管理数据库会话、聚合Configuration的属性。

Configuration:是全局所有的运行数据配置,相当于一个大的仓库。

XMLConfigBuilder:解析XML的Configuration构建器。

XMLMapperBuilder:解析XML的Mapper构建器。

根据时序图我们可以看到实际上分为三个过程【创建】-》【解析】-》【构建】

3.3.1、SqlSessionFactoryBuilder源码分析

public class SqlSessionFactoryBuilder {
//根据字符流构建会话工厂
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
...省略本次无关内容...
//根据字符流和环境和属性构建会话工厂
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
//利用xml配置构建器
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.
}
}
}
...省略本次无关内容...
//根据全局配置构建会话工厂,走默认的会话工厂
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}



3.3.2、SqlSessionFactory源码分析

3.3.2.1、SqlSessionFactory源码分析



/*
*从连接或数据源创建SqlSession
*/
public interface SqlSessionFactory {
//打开会话
SqlSession openSession();
//打开会话是否自动提交
SqlSession openSession(boolean autoCommit);
//打开链接的session
SqlSession openSession(Connection connection);
//打开事务隔离级别的session
SqlSession openSession(TransactionIsolationLevel level);
//打开执行器类型的session
SqlSession openSession(ExecutorType execType);
//打开执行器类型,是否自动提交的session
SqlSession openSession(ExecutorType execType, boolean autoCommit);
//打开执行器和事务隔离级别机制的session
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
//打开执行器,链接的session
SqlSession openSession(ExecutorType execType, Connection connection);
//获取全局配置
Configuration getConfiguration();
}



3.3.2.2、DefaultSqlSessionFactory源码分析

/**
* 默认的会话工厂
* @author Clinton Begin
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
//全局配置
private final Configuration configuration;
//构造函数
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
...省略本次无关内容...
}



3.3.3、Configuration源码分析



/**
* 抽象构建器
* @author Clinton Begin
*/
public abstract class BaseBuilder {
//全局配置
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();
}
//获取全局配置
public Configuration getConfiguration() {
return configuration;
}
//解析正则表达式
protected Pattern parseExpression(String regex, String defaultValue) {
return Pattern.compile(regex == null ? defaultValue : regex);
}
//获取boolean值
protected Boolean booleanValueOf(String value, Boolean defaultValue) {
return value == null ? defaultValue : Boolean.valueOf(value);
}
//获取int值
protected Integer integerValueOf(String value, Integer defaultValue) {
return value == null ? defaultValue : Integer.valueOf(value);
}
//获取set值
protected Set<String> stringSetValueOf(String value, String defaultValue) {
value = value == null ? defaultValue : value;
return new HashSet<>(Arrays.asList(value.split(",")));
}
//解析jdbc类型
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);
}
}
//解析结果集类型
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);
}
}
//解析参数风格
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);
}
}
//创建实例
protected Object createInstance(String alias) {
Class<?> clazz = resolveClass(alias);
if (clazz == null) {
return null;
}
try {
return clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new BuilderException("Error creating instance. Cause: " + e, e);
}
}
//根据别名解析类
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 TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) {
if (typeHandlerAlias == null) {
return null;
}
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
Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type;
return resolveTypeHandler(javaType, typeHandlerType);
}
//解析类型处理器
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<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
if (handler == null) {
// not in registry, create a new one
handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
}
return handler;
}
//解析别名
protected <T> Class<? extends T> resolveAlias(String alias) {
return typeAliasRegistry.resolveAlias(alias);
}
}



3.3.4.1、XMLConfigBuilder源码分析

/**
* xml配置构建器
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public class XMLConfigBuilder extends BaseBuilder {
//是否解析
private boolean parsed;
//解析器
private final XPathParser parser;
//环境
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);
}
//构造函数
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) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
//解析全局配置对象
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//解析全局配置对象
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//解析属性变量节点
propertiesElement(root.evalNode("properties"));
//解析setting节点字段
Properties settings = settingsAsProperties(root.evalNode("settings"));
//加载用户自定义的VFS解析文件类
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"));
//数据库ID
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//类型处理器元素
typeHandlerElement(root.evalNode("typeHandlers"));
//mapper接口元素
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
//设置属性
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
//检查所有的setting设置是在配置类中已知的
// 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))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
//加载定制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);
}
}
}
}
//加载定制实现类
private void loadCustomLogImpl(Properties props) {
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
configuration.setLogImpl(logImpl);
}
//类型别名元素
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);
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);
}
}
}
}
}
//插件元素
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).getDeclaredConstructor().newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
//对象工厂元素
private void objectFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties properties = context.getChildrenAsProperties();
ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(properties);
configuration.setObjectFactory(factory);
}
}
//对象包装工厂元素
private void objectWrapperFactoryElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
configuration.setObjectWrapperFactory(factory);
}
}