和 Spring 框架 的 IoC 容器初始化 一样,Mybatis 也会通过定位、解析相应的配置文件完成自己的初始化。Mybatis 的配置文件主要有 mybatis-config.xml 核心配置文件 及一系列映射配置文件,另外,Mybatis 也会根据注解进行配置。
1 BaseBuilder
Mybatis 初始化 的主要内容是加载并解析 mybatis-config.xml 配置文件、映射配置文件以及相关的注解信息。Mybatis 的初始化入口是 SqlSessionFactoryBuilder 的 build()方法。
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);
}
/**
* build()方法 的主要实现
*/
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// SqlSessionFactory 会创建 XMLConfigBuilder对象 来解析 mybatis-config.xml配置文件
// XMLConfigBuilder 继承自 BaseBuilder抽象类,顾名思义这一系的类使用了 建造者设计模式
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 解析配置文件的内容 到 Configuration对象,根据 Configuration对象
// 创建 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.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
BaseBuilder 中的核心字段如下:
public abstract class BaseBuilder {
// 保存了 Mybatis 的几乎所以核心配置信息,全局唯一
protected final Configuration configuration;
// 在 mybatis-config.xml 中可以通过 <typeAliases>标签 定义别名
protected final TypeAliasRegistry typeAliasRegistry;
// 在 mybatis-config.xml 中可以通过 <typeHandlers>标签 添加 自定义TypeHandler
// TypeHandler 用于完成 JDBC数据类型 与 Java类型 的相互转换,所有的 TypeHandler
// 都保存在 typeHandlerRegistry 中
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
}
BaseBuilder 中的 typeAliasRegistry 和 typeHandlerRegistry 字段 均来自于 configuration,通过 BaseBuilder 的构造方法可以看到详细内容。
2 XMLConfigBuilder
XMLConfigBuilder 是 BaseBuilder 的众多子类之一,主要负责解析 mybatis-config.xml 配置文件。它通过调用 parseConfiguration()方法 实现整个解析过程,其中,mybatis-config.xml 配置文件 中的每个节点都被封装成了一个个相应的解析方法,parseConfiguration()方法 只是依次调用了这些解析方法而已。
public class XMLConfigBuilder extends BaseBuilder {
// 标记是否解析过 mybatis-config.xml文件
private boolean parsed;
// 用于解析 mybatis-config.xml 的解析器
private final XPathParser parser;
// 标识 <environment>配置 的名称,默认读取 <environment>标签 的 default属性
private String environment;
// 创建并缓存 Reflector对象
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
/**
* 解析的入口,调用了 parseConfiguration() 进行后续的解析
*/
public Configuration parse() {
// parsed标志位 的处理
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 在 mybatis-config.xml配置文件 中查找 <configuration>节点,并开始解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
// 根据 root.evalNode("properties") 中的值就可以知道具体是解析哪个标签的方法咯
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(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);
}
}
Mybatis 中的标签很多,所以相对应的解析方法也很多,这里挑几个比较重要的标签进行分析。
2.1 解析<typeHandlers>标签
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
// 处理 <typeHandlers> 下的所有子标签
for (XNode child : parent.getChildren()) {
// 处理 <package> 标签
if ("package".equals(child.getName())) {
// 获取指定的包名
String typeHandlerPackage = child.getStringAttribute("name");
// 通过 typeHandlerRegistry 的 register(packageName)方法
// 扫描指定包中的所有 TypeHandler类,并进行注册
typeHandlerRegistry.register(typeHandlerPackage);
} else {
// Java数据类型
String javaTypeName = child.getStringAttribute("javaType");
// JDBC数据类型
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) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry