MyBatis 3.5.4源码之旅二之初始化流程二
对照的流程图
XMLConfigBuilder的parse
接下去就是最重要的build(parser.parse())
了,我们先看parser.parse()
。
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));//获取configuration结点的数据
return configuration;
}
XMLConfigBuilder的parseConfiguration解析配置文件
一看就知道要开始解析xml
了,从configuration
节点开始。主要的解析流程就在这里了:
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
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"));
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);
}
}
XMLConfigBuilder的settingsAsProperties解析设置配置
有那么多要解析和处理的,我就不全讲了,讲几个我们经常用到的,比如解析settings
,我们要设置二级缓存settingsAsProperties
:
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
//获取属性 比如lazyLoadingEnabled
Properties props = context.getChildrenAsProperties();
// 检查是否是合法的,就是能识别的,用了mybatis不能识别的就报错啦
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;
}
XMLConfigBuilder的typeAliasesElement解析别名
再来看看别名解析typeAliasesElement
,分package
和另外一种,但是注册时候是一样的,package
只是还需要解析出包下的类的Class
对象:
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);
}
}
}
}
}
其中主要是configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
:
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
//获取包下的所有类的Class对象
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
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);
}
}
}
获取包下的所有类resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
:
public ResolverUtil<T> find(Test test, String packageName) {
String path = getPackagePath(packageName);
try {
//用类加载器去获取包下字节码文件路径名 com/xxx/xx/xx.class
List<String> children = VFS.getInstance().list(path);
for (String child : children) {
if (child.endsWith(".class")) {
//从路径名解析出对应的Class对象
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
解析出对应的Class
对象addIfMatching
放入HashSet
中:
protected void addIfMatching(Test test, String fqn) {
try {
String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
ClassLoader loader = getClassLoader();
if (log.isDebugEnabled()) {
log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
}
Class<?> type = loader.loadClass(externalName);
if (test.matches(type)) {
matches.add((Class<T>) type);
}
} catch (Throwable t) {
log.warn("Could not examine class '" + fqn + "'" + " due to a " +
t.getClass().getName() + " with message: " + t.getMessage());
}
}
然后取出来所有对象进行注册:
获取简单类名,就是没有包含包名的,注册过程中发现有注解别名的,优先使用注解的别名:
最后把别名小写当做key
,Class
对象当做Value
注册进去:
本来还想讲mapperElement(root.evalNode("mappers"));
,但是这个比较复杂,篇幅就巨长了,所以打算留着下次讲。
总结下,初始化做的大致事情:
- 加载配置文件
- 创建
SqlSessionFactory
- 创建
XMLConfigBuilder
- 解析配置文件
当然具体的肯定会非常细,这个我觉得我也不可能带着全部走完,还是需要出现问题,或者有时间可以debug
跟进去看看,了解下源码的大致脉络。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。