核心配置中${}表达式配置的解析
- 一、核心配置主体
- 二、核心配置文件中properties是如何被解析的?
- 三、${} 表达式的解析
- 四、总结
前提:
核心配置文件是被XMLConfigBuilder 对象进行解析的,configuration 对象是由它父类BaseBuider继承下来的属性。
XMLConfigBuilder 对象解析完配置文件,其信息是被封装在了configuration 对象中,
然后返回,通过SqlSessionFactoryBuilder 去通过build(configuration)方法进行构建SqlSessionFactory对象,
一个数据库是关联一个environment 的,所以是一个sqlSessionFactory 对象对应一个数据库,实际上也对应一个configuration 对象........
一、核心配置主体
配置信息的配置主体先进行阐明:
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; //源码中没有这一句,只有 parseConfiguration(parser.evalNode("/configuration")); //为了让读者看得更明晰,源码拆分为以下两句 XNode configurationNode = parser.evalNode("/configuration"); parseConfiguration(configurationNode); return configuration;
}
/** * 解析 "/configuration"节点下的子节点信息,然后将解析的结果设置到Configuration对象中 */
private void parseConfiguration(XNode root) { try { //1.首先处理properties 节点 propertiesElement(root.evalNode("properties")); //issue #117 read properties first //2.处理typeAliases typeAliasesElement(root.evalNode("typeAliases")); //3.处理插件 pluginElement(root.evalNode("plugins")); //4.处理objectFactory objectFactoryElement(root.evalNode("objectFactory")); //5.objectWrapperFactory objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //6.settings settingsElement(root.evalNode("settings")); //7.处理environments environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631 //8.database databaseIdProviderElement(root.evalNode("databaseIdProvider")); //9.typeHandlers typeHandlerElement(root.evalNode("typeHandlers")); //10.mappers mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); }
}
再看核心配置文件中DTD约束,不难看出其解析顺序和约束是一致的。这些解析出来的结果最后都会封装到configuration对象中。
二、核心配置文件中properties是如何被解析的?
properties的三种配置方式:
- 通过property 子标签,进行name<==>value进行配置;
- 通过url 属性(外配置文件);
- 通过resource 属性(外配置文件)。为契合项目路径,这种方式使用的多。
下面阅读已被吾注解好了的解析properties 的代码
private void propertiesElement(XNode context) throws Exception {if (context != null) {// 第一种配置方式Properties defaults = context.getChildrenAsProperties();// 第三种利用resource配置String resource = context.getStringAttribute("resource");// 第二种利用url进行配置String url = context.getStringAttribute("url");// 第二种和第三种配置不能同时存在// 意思就是resource 属性和 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.");}// 下面就是分别对resource和url进行判断了if (resource != null) {defaults.putAll(Resources.getResourceAsProperties(resource));} else if (url != null) {defaults.putAll(Resources.getUrlAsProperties(url));}// 这个是看原先有没有对cofiguration中的variables属性赋值// 如果有的话一并加入到defaults这个对象中Properties vars = configuration.getVariables();if (vars != null) {defaults.putAll(vars);}// parser 是一个XPathParser的对象;其中有variable是其中的一个属性// variable 是Properties 对象;// 先对parser 的variable的属性进行赋值,后面用于 对datasource 的配置有用parser.setVariables(defaults);// 解析的结果最后得在configuration中,所以....configuration.setVariables(defaults);}}
这里需要注意的有两点:
- SqlSessionBuilder执行build方法的时候,也是可以传一个Properties 对象的,这个对象会在XMLConfigBuilder对象创建的时候赋值给configuration对象,这也就是上面源码那个为什么要去判断一下有没有提前赋值(上面的
vars
)。 - 这里defaults 对象虽然不允许 url 和 resource 属性值都时接受,但是允许接受完 url 或 resource的配置文件后还可以加上第一种配置产生的值,还可以接受 build 传过来的配置,非常的灵活。
Configuration
对象中的set、get方法是对外提供的,当然也可以自行对其进行修改和获取。当然没啥事谁修改这玩意啊。
三、${} 表达式的解析
回到根本,${} 到底是如何解析的呢?
首先需要了解 XNode 类中的 evalNode(String expression)
方法。
这个 XPathParser 对象都是使用的 XMLConfigBuilder 内的属性 parser 对象。
variables 解析其他标签也都是共享的,一级传一级的。
所以也就是说解析 datasource 是可以使用共享的 variables 的。
然后就可以看解析的 environmentElement 了,看看。
解析这个 '${}‘ 的核心代码如下:
/*** 这个类解析${}这种形式的表达式*/
public class PropertyParser {public static String parse(String string, Properties variables) {VariableTokenHandler handler = new VariableTokenHandler(variables);GenericTokenParser parser = new GenericTokenParser("${", "}", handler);return parser.parse(string);}private static class VariableTokenHandler implements TokenHandler {private Properties variables;public VariableTokenHandler(Properties variables) {this.variables = variables;}public String handleToken(String content) {if (variables != null && variables.containsKey(content)) {return variables.getProperty(content);}return "${" + content + "}";}}
}
四、总结
- 解析顺序和DTD约束是一致的;
- properties 配置的解析及其三种配置方式,resource和url不可以共存,但使用property子标签配置可以共存;
- XMLConfigBuilder 中的 XPathParser 类型对象 parser 属性,在解析过程中一直使用的是同一个,并且所解析的 variables 那个配置也一直在传递;
- variables 可以是properties 中所写的配置,也可以是调用SqlSessionBuilder对象中的build方法进行传入;
- ${content} 是通过字符串处理的方式和从 variables 查询 content 的方式进行处理的。