我们在使用 MyBatis 框架时,通常会进行一定的设置,使其能更好的满足我们的需求。对于一个框架来说,提供较为丰富的配置文件,也是其灵活性的体 现。本文将会介绍 MyBatis 配置文件中的大部分节点解析过程。
我们在使用 MyBatis 时,第一步要做的事情一般是根据配置文件构建 SqlSessionFactory 对象。相关代码大致如下:
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
在上面代码中,我们首先会使用 MyBatis 提供的工具类 Resources 加载配置文件,得到 一个输入流。然后再通过 SqlSessionFactoryBuilder 对象的 build 方法构建 SqlSessionFactory 对象。这里的 build 方法是我们分析配置文件解析过程的入口方法。下面我们来看一下这个:
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
从上面的代码中,我们大致可以猜出 MyBatis 配置文件是通过 XMLConfigBuilder 进行解析的。不过目前这里还没有非常明确的解析逻辑,所以我们继续往下看。这次来看一下 XMLConfigBuilder 的 parse 方法,如下:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
到这里大家可以看到一些端倪了,注意一个 xpath 表达式—— /configuration。这个表达式代表的是 MyBatis 配置文件的
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//解析properties结点
propertiesElement(root.evalNode("properties"));
//解析 settings 配置,并将其转换为 Properties 对象
Properties settings = settingsAsProperties(root.evalNode("settings"));
//加载vfs
loadCustomVfs(settings);
//解析typeAliases结点
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);
}
}
解析properties结点:
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//获取子节点,并转换为properties对象
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.");
}
//如果只存在一个,则解析转换为properties对象存储起来
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);
}
parser.setVariables(defaults);
//放到configuration里
configuration.setVariables(defaults);
}
}
public Properties getChildrenAsProperties() {
Properties properties = new Properties();
for (XNode child : getChildren()) {
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
}
public ListgetChildren() { List children = new ArrayList (); NodeList nodeList = node.getChildNodes(); if (nodeList != null) { for (int i = 0, n = nodeList.getLength(); i < n; i++) { Node node = nodeList.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { children.add(new XNode(xpathParser, node, variables)); } } } return children; }
上面就是
需要注意的是,propertiesElement 方法是先解析
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
// 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;
}
解析 settings 子节点的内容,并将解析结果转成 Properties 对象
为Configuration 创建元信息对象
通过 MetaClass 检测 Configuration 中是否存在某个属性的 setter 方法,
不存在则抛异常
若通过 MetaClass 的检测,则返回 Properties 对象,方法逻辑结束
MetaClass对象创建过程:
元信息类 MetaClass的构造方法为私有类型,所以不能直接创建,必须使用其提供的 forClass 方法进行创建。它的创建逻辑如下:
public class MetaClass {
private final ReflectorFactory reflectorFactory;
private final Reflector reflector;
private MetaClass(Class> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
this.reflector = reflectorFactory.findForClass(type);
}
}
public static MetaClass forClass(Class> type, ReflectorFactory reflectorFactory) {
return new MetaClass(type, reflectorFactory);
}
上面代码出现了两个新的类 ReflectorFactory 和 Reflector,MetaClass 通过引入这些新类帮助它完成功能。下面我们看一下 hasSetter 方法的源码就知道是怎么回事了。
public boolean hasSetter(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
if (reflector.hasSetter(prop.getName())) {
MetaClass metaProp = metaClassForProperty(prop.getName());
return metaProp.hasSetter(prop.getChildren());
} else {
return false;
}
} else {
return reflector.hasSetter(prop.getName());
}
}
从上面的代码中,我们可以看出 MetaClass 中的 hasSetter 方法最终调用了 Reflector 的 hasSetter 方法。
DefaultReflectorFactory
DefaultReflectorFactory 用于创建 Reflector,同时兼有缓存的功能,它的源码如下:
public class DefaultReflectorFactory implements ReflectorFactory {
private boolean classCacheEnabled = true;
private final ConcurrentMap, Reflector> reflectorMap = new ConcurrentHashMap, Reflector>();
@Override
public Reflector findForClass(Class> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
Reflector cached = reflectorMap.get(type);
if (cached == null) {
cached = new Reflector(type);
reflectorMap.put(type, cached);
}
return cached;
} else {
return new Reflector(type);
}
}
}
Reflector
public class Reflector {
private final Class> type;
private final String[] readablePropertyNames;
private final String[] writeablePropertyNames;
private final Map setMethods = new HashMap();
private final Map getMethods = new HashMap();
private final Map> setTypes = new HashMap>();
private final Map> getTypes = new HashMap>();
private Constructor> defaultConstructor;
private Map caseInsensitivePropertyMap =
new HashMap();
// 解析目标类的默认构造方法,并赋值给 defaultConstructor 变量
public Reflector(Class> clazz) {
type = clazz;
addDefaultConstructor(clazz);
// 解析 getter 方法,并将解析结果放入 getMethods 中
addGetMethods(clazz);
// 解析 setter 方法,并将解析结果放入 setMethods 中
addSetMethods(clazz);
// 解析属性字段,并将解析结果添加到 setMethods 或 getMethods 中
addFields(clazz);
// 从 getMethods 映射中获取可读属性名数组
readablePropertyNames = getMethods.keySet()
.toArray(new String[getMethods.keySet().size()]);
// 从 setMethods 映射中获取可写属性名数组
writeablePropertyNames = setMethods.keySet()
.toArray(new String[setMethods.keySet().size()]);
// 将所有属性名的大写形式作为键,属性名作为值,
// 存入到 caseInsensitivePropertyMap 中
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap .put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName .toUpperCase(Locale.ENGLISH), propName);
}
}
getter 方法解析的逻辑被封装在了 addGetMethods 方法中,这个方法除了会解析形如 getXXX 的方法,同时也会解析 isXXX 方法。该方法的源码分析如下:
private void addGetMethods(Class> cls) {
Map> conflictingGetters = new HashMap>();
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
// getter 方法不应该有参数,若存在参数,则忽略当前方法
if (method.getParameterTypes().length > 0) {
continue;
}
String name = method.getName();
// 过滤出以 get 或 is 开头的方法
if ((name.startsWith("get") && name.length() > 3)
|| (name.startsWith("is") && name.length() > 2)) {
// 将 getXXX 或 isXXX 等方法名转成相应的属性,比如 getName -> name
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingGetters, name, method);
}
}
//解决冲突
resolveGetterConflicts(conflictingGetters);
}
addMethodConflict(conflictingGetters, name, method)方法源码:
private void addMethodConflict(Map> conflictingMethods, String name, Method method) { List list = conflictingMethods.get(name); if (list == null) { list = new ArrayList (); conflictingMethods.put(name, list); } list.add(method); }
解决冲突:
private void resolveGetterConflicts(Map> conflictingGetters) { for (Entry > entry : conflictingGetters.entrySet()) { Method winner = null; String propName = entry.getKey(); for (Method candidate : entry.getValue()) { if (winner == null) { winner = candidate; continue; } Class> winnerType = winner.getReturnType(); Class> candidateType = candidate.getReturnType(); if (candidateType.equals(winnerType)) { if (!boolean.class.equals(candidateType)) { throw new ReflectionException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + winner.getDeclaringClass() + ". This breaks the JavaBeans specification and can cause unpredictable results."); } else if (candidate.getName().startsWith("is")) { winner = candidate; } } else if (candidateType.isAssignableFrom(winnerType)) { // OK getter type is descendant } else if (winnerType.isAssignableFrom(candidateType)) { winner = candidate; } else { throw new ReflectionException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + winner.getDeclaringClass() + ". This breaks the JavaBeans specification and can cause unpredictable results."); } } // 将筛选出的方法添加到 getMethods 中,并将方法返回值添加到 getTypes 中 addGetMethod(propName, winner); } }
private void addGetMethod(String name, Method method) {
if (isValidPropertyName(name)) {
getMethods.put(name, new MethodInvoker(method));
// 解析返回值类型
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// 将返回值类型由 Type 转为 Class,并将转换后的结果缓存到 setTypes 中
getTypes.put(name, typeToClass(returnType));
}



