这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战
时序图
sequenceDiagram
participant S as Client
participant A as SqlSessionFactoryBuilder
participant B as XMLConfigBuilder
participant C as XPathParser
S ->>+ A: build()
A ->>+ B: new
B ->>+ C: new
C ->> C: createDocument():Document
C -->>- B: XPathParser
A ->> B: parse()
B ->> B : parseConfiguration()
B -->>- A : Configuration
A ->> A : build()
A -->>- S : DefaultSqlSessionFactory
步骤详解
获取SqlSessionFactory的测试代码
public SqlSessionFactory getSqlSessionFactory() {
// 指定配置文件所处的位置
String resource = "resources/mybatis-config.xml";
// 读取配置文件为输入流
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
// 使用输入流创建SqlSessionFactory
return new SqlSessionFactoryBuilder().build(inputStream);
}
进入到SqlSessionFactoryBuilder的build方法中
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 使用输入流创建XMLConfigBuilder对象,在此过程中进行XML文档进行DOM解析,保存在XMLConfigBuilder对象中
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 调用解析方法获取Configuration对象,并使用Configuration对象创建一个DefaultSqlSessionFactory对象并返回
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
进入XMLConfigBuilder的构造方法中
/**
* 根据传入的输入流、环境参数、属性参数构建XMLConfigBuilder
*/
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
/**
* 1. 根据输入流创建XPathParser对象,并生成Document存放在XPathParser对象中
* 2. 根据XPathParser及环境参数、属性参数构建XMLConfigBuilder
*/
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
/**
* 1. 调用父类BaseBuilder的构造方法,并传入Configuration对象
* 2. 将参数中的环境参数、属性参数传入创建的Configuration对象中
*/
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;
}
BaseBuilder类的字段及构造方法
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();
}
}
进入XMLConfigBuilder的parse方法中,再到parseConfiguration方法,这里就是解析配置文件的地方
/**
* 从根节点configuration开始解析下层节点
* @param root 根节点configuration节点
*/
private void parseConfiguration(XNode root) {
try {
/**
* 所有的配置在解析完成后都会放入到此对象内部的Configuration对象中保存
*/
// 首先解析properties,以保证在解析其他节点时便可以生效
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
// 扫描settings标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
//加载自定义类扫描器
loadCustomVfs(settings);
//加载自定义日志实现
loadCustomLogImpl(settings);
//别名扫描注册
typeAliasesElement(root.evalNode("typeAliases"));
//插件扫描
pluginElement(root.evalNode("plugins"));
//解析Pojo对象工厂类
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//解析settings标签
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//解析环境标签
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析类型转换器
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mappers
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
解析properties标签
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 读取properties标签下的property标签
Properties defaults = context.getChildrenAsProperties();
// 根据配置的resource属性或url属性的值读取配置内容
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
// 将读取到的配置内容存放在XPathParser对象的variables变量中和Configuration对象的variables变量中
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
解析typeAliases标签,别名扫描注册
XMLConfigBuilder#typeAliasesElement
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 解析配置的包路径
String typeAliasPackage = child.getStringAttribute("name");
// 调用TypeAliasRegistry#registerAliases方法进行别名注册
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
// 解析单独配置的别名typeAlias标签
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);
}
}
}
}
}
TypeAliasRegistry#registerAliases,在注册别名时,Mybatis将所有的别名都转换成了小写,因此在Mybatis中,别名是大小写不敏感的
public void registerAliases(String packageName) {
// 首次进入,默认superType为Object
registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType) {
// 工具类ResolverUtil
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
/**
* 1. 通过superType构建ResolverUtil工具类的内部类IsA对象
* 2. 调用ResolverUtil类的find方法筛选所有的类,并保存在matches属性中
*/
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
// 获取matches属性中保存的类
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
// 为所有类注册别名
for (Class<?> type : typeSet) {
// Ignore inner classes and interfaces (including package-info.java)
// Skip also inner classes. See issue #6
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
}
}
public void registerAlias(Class<?> type) {
// 获取类名
String alias = type.getSimpleName();
// 解析类上的Alias注解
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
// 别名注册
registerAlias(alias, type);
}
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// issue #748
// 将别名全部转换为小写
String key = alias.toLowerCase(Locale.ENGLISH);
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
}
// 注册
typeAliases.put(key, value);
}
ResolverUtil#find
/**
* 筛选出指定路径下符合一定条件的类
* @param test 测试条件
* @param packageName 路径
* @return 本身
*/
public ResolverUtil<T> find(Test test, String packageName) {
// 获取起始包路径
String path = getPackagePath(packageName);
try {
/**
* 1. 通过单例模式获取VFS实例
* 2. 调用VFS接口的list方法获取包中所有的文件
*/
List<String> children = VFS.getInstance().list(path);
for (String child : children) {
// 对类文件进行测试
if (child.endsWith(".class")) { // 必须是类文件
// 测试是否满足测试条件,如果满足,则将该类文件记录下来
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
VFS扫描文件夹
// 将url转换为InputStream
InputStream is = url.openStream();
// 将InputStream包装成BufferedReader
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
List<String> lines = new ArrayList<>();
// 利用BufferedReader的readLine方法,读取文件夹下的文件
for (String line; (line = reader.readLine()) != null; ) {
if (log.isDebugEnabled()) {
log.debug("Reader entry: " + line);
}
lines.add(line);
if (getResources(path + "/" + line).isEmpty()) {
lines.clear();
break;
}
}
解析typeHandlers标签
private void typeHandlerElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 根据配置的包名扫描,获取TypeHandler接口所有的实现类,并将这些类型转换器进行注册
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
// 解析typeHandler标签属性
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);
/**
* 注册类型转换器到Map<Type, Map<JdbcType, TypeHandler<?>>>容器中
* 容器中将JavaType,JdbcType,TypeHandler进行映射
*/
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
解析mappers标签
- 下文详解
SqlSessionFactoryBuilder#build方法,根据Configuration对象创建并返回一个DefaultSqlSessionFactory对象
/**
* 根据配置信息建造一个SqlSessionFactory对象
* @param config 配置信息
* @return SqlSessionFactory对象
*/
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
以上便是对Mybatis中SqlSessionFactory对象的创建过程源码的分析。这部分内容比较简单,只需要了解一下大致的流程步骤即可。