【Mybatis 系列10-结合源码解析mybatis 执行流程】

【Mybatis 系列9-强大的动态sql 语句】

【Mybatis 系列8-结合源码解析select、resultMap的用法】

【Mybatis 系列7-结合源码解析核心CRUD配置及用法】

【Mybatis 系列6-结合源码解析节点配置objectFactory、databaseIdProvider、plugins、mappers】

【Mybatis 系列5-结合源码解析TypeHandler】

【Mybatis 系列4-结合源码解析节点typeAliases】

【Mybatis 系列3-结合源码解析properties节点和environments节点】

【Mybatis 系列2-配置文件】

【Mybatis 系列1-环境搭建】

系列文章 2 中,我们通过对mybatis源码的简单分析,可看出,在mybatis配置文件中,在configuration根节点下面,可配置properties、typeAliases、plugins、objectFactory、objectWrapperFactory、settings、environments、databaseIdProvider、typeHandlers、mappers这些节点。

那么本次,就会先介绍properties节点和environments节点。

为了让大家能够更好地阅读mybatis源码,我先简单的给大家示例一下properties的使用方法。

 <configuration>
2 <!-- 方法一: 从外部指定properties配置文件, 除了使用resource属性指定外,还可通过url属性指定url
3 <properties resource="dbConfig.properties"></properties>
4 -->
5 <!-- 方法二: 直接配置为xml -->
6 <properties>
7 <property name="driver" value="com.mysql.jdbc.Driver"/>
8 <property name="url" value="jdbc:mysql://localhost:3306/test1"/>
9 <property name="username" value="root"/>
10 <property name="password" value="root"/>
11 </properties>

那么,我要是 两种方法都同时用了,那么哪种方法优先?

当以上两种方法都xml配置优先, 外部指定properties配置其次。

为什么呢,接下来的源码分析会提到,请留意一下。

再看一下envirements元素节点的使用方法吧:

 <environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--
如果上面没有指定数据库配置的properties文件,那么此处可以这样直接配置
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
--> <!-- 上面指定了数据库配置文件, 配置文件里面也是对应的这四个属性 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/> </dataSource>
</environment> <!-- 我再指定一个environment -->
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!-- 与上面的url不一样 -->
<property name="url" value="jdbc:mysql://localhost:3306/demo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment> </environments>

environments元素节点可以配置多个environment子节点, 怎么理解呢?

假如我们系统的开发环境和正式环境所用的数据库不一样(这是肯定的), 那么可以设置两个environment, 两个id分别对应开发环境(dev)和正式环境(final),那么通过配置environments的default属性就能选择对应的environment了, 例如,我将environments的deault属性的值配置为dev, 那么就会选择dev的environment。

这个是怎么实现的呢?

下面看源码。

上面简单给大家介绍了一下properties 和 environments 的配置, 接下来就正式开始看源码了:

上次我们说过mybatis 是通过XMLConfigBuilder这个类在解析mybatis配置文件的, 那么本次就接着看看XMLConfigBuilder对于properties和environments的解析:

 XMLConfigBuilder:

 public class XMLConfigBuilder extends BaseBuilder {
2
3 private boolean parsed;
4 //xml解析器
5 private XPathParser parser;
6 private String environment;
7
8 //上次说到这个方法是在解析mybatis配置文件中能配置的元素节点
9 //今天首先要看的就是properties节点和environments节点
10 private void parseConfiguration(XNode root) {
11 try {
12 //解析properties元素
13 propertiesElement(root.evalNode("properties")); //issue #117 read properties first
14 typeAliasesElement(root.evalNode("typeAliases"));
15 pluginElement(root.evalNode("plugins"));
16 objectFactoryElement(root.evalNode("objectFactory"));
17 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
18 settingsElement(root.evalNode("settings"));
19 //解析environments元素
20 environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
21 databaseIdProviderElement(root.evalNode("databaseIdProvider"));
22 typeHandlerElement(root.evalNode("typeHandlers"));
23 mapperElement(root.evalNode("mappers"));
24 } catch (Exception e) {
25 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
26 }
27 }
28
29
30 //下面就看看解析properties的具体方法
31 private void propertiesElement(XNode context) throws Exception {
32 if (context != null) {
33 //将子节点的 name 以及value属性set进properties对象
34 //这儿可以注意一下顺序,xml配置优先, 外部指定properties配置其次
35 Properties defaults = context.getChildrenAsProperties();
36 //获取properties节点上 resource属性的值
37 String resource = context.getStringAttribute("resource");
38 //获取properties节点上 url属性的值, resource和url不能同时配置
39 String url = context.getStringAttribute("url");
40 if (resource != null && url != null) {
41 throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
42 }
43 //把解析出的properties文件set进Properties对象
44 if (resource != null) {
45 defaults.putAll(Resources.getResourceAsProperties(resource));
46 } else if (url != null) {
47 defaults.putAll(Resources.getUrlAsProperties(url));
48 }
49 //将configuration对象中已配置的Properties属性与刚刚解析的融合
50 //configuration这个对象会装载所解析mybatis配置文件的所有节点元素,以后也会频频提到这个对象
51 //既然configuration对象用有一系列的get/set方法, 那是否就标志着我们可以使用java代码直接配置?
52 //答案是肯定的, 不过使用配置文件进行配置,优势不言而喻
53 Properties vars = configuration.getVariables();
54 if (vars != null) {
55 defaults.putAll(vars);
56 }
57 //把装有解析配置propertis对象set进解析器, 因为后面可能会用到
58 parser.setVariables(defaults);
59 //set进configuration对象
60 configuration.setVariables(defaults);
61 }
62 }
63
64 //下面再看看解析enviroments元素节点的方法
65 private void environmentsElement(XNode context) throws Exception {
66 if (context != null) {
67 if (environment == null) {
68 //解析environments节点的default属性的值
69 //例如: <environments default="development">
70 environment = context.getStringAttribute("default");
71 }
72 //递归解析environments子节点
73 for (XNode child : context.getChildren()) {
74 //<environment id="development">, 只有enviroment节点有id属性,那么这个属性有何作用?
75 //environments 节点下可以拥有多个 environment子节点
76 //类似于这样: <environments default="development"><environment id="development">...</environment><environment id="test">...</environments>
77 //意思就是我们可以对应多个环境,比如开发环境,测试环境等, 由environments的default属性去选择对应的enviroment
78 String id = child.getStringAttribute("id");
79 //isSpecial就是根据由environments的default属性去选择对应的enviroment
80 if (isSpecifiedEnvironment(id)) {
81 //事务, mybatis有两种:JDBC 和 MANAGED, 配置为JDBC则直接使用JDBC的事务,配置为MANAGED则是将事务托管给容器,
82 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
83 //enviroment节点下面就是dataSource节点了,解析dataSource节点(下面会贴出解析dataSource的具体方法)
84 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
85 DataSource dataSource = dsFactory.getDataSource();
86 Environment.Builder environmentBuilder = new Environment.Builder(id)
87 .transactionFactory(txFactory)
88 .dataSource(dataSource);
89 //老规矩,会将dataSource设置进configuration对象
90 configuration.setEnvironment(environmentBuilder.build());
91 }
92 }
93 }
94 }
95
96 //下面看看dataSource的解析方法
97 private DataSourceFactory dataSourceElement(XNode context) throws Exception {
98 if (context != null) {
99 //dataSource的连接池
100 String type = context.getStringAttribute("type");
101 //子节点 name, value属性set进一个properties对象
102 Properties props = context.getChildrenAsProperties();
103 //创建dataSourceFactory
104 DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
105 factory.setProperties(props);
106 return factory;
107 }
108 throw new BuilderException("Environment declaration requires a DataSourceFactory.");
109 }
110 }

通过以上对mybatis源码的解读,相信大家对mybatis的配置又有了一个深入的认识。

还有一个问题, 上面我们看到,在配置dataSource的时候使用了 ${driver} 这种表达式

这种形式是怎么解析的?

其实,是通过PropertyParser这个类解析:

PropertyParser:

 /**
* 这个类解析${}这种形式的表达式
*/
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 + "}";
}
}
}

好啦,以上就是对于properties 和 environments元素节点的分析,比较重要的都在对于源码的注释中标出。

本次文章到此结束,接下来的文章会继续分析其他节点的配置。

Mybatis 系列3-结合源码解析properties节点和environments节点的更多相关文章

  1. Mybatis 系列10-结合源码解析mybatis 的执行流程

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  2. Mybatis 系列8-结合源码解析select、resultMap的用法

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  3. Mybatis 系列7-结合源码解析核心CRUD 配置及用法

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  4. Mybatis 系列6-结合源码解析节点配置:objectFactory、databaseIdProvider、plugins、mappers

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  5. Mybatis 系列5-结合源码解析TypeHandler

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  6. Mybatis 系列4-结合源码解析节点:typeAliases

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  7. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

    问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...

  8. 死磕 java同步系列之Phaser源码解析

    问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...

  9. 死磕 java同步系列之StampedLock源码解析

    问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...

随机推荐

  1. mysql 之mvcc多版本控制

    MVCC是multiversion concurrency control的缩写,提供MySQL事物隔离级别下无锁读,例如一个事物在执行update等修改数据的sql,并未提交时其他事物进行数据读取是 ...

  2. System.Windows.Forms.Timer、System.Timers.Timer、System.Threading.Timer的 区别和用法

    System.Windows.Forms.Timer执行的时候,如果你在过程中间加一个sleep整个的界面就死掉了,但是另外两个没有这个情况,System.Timers.Timer.System.Th ...

  3. 原码,反码,补码,及Java中数字表示方法

    原码:原码是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 如:如果是八位二进制1即用00000001表示,-1即用10000001表示. 反码:正数的反码就是其本身,负数的反码是在其 ...

  4. WebSocket概念

    WebSocket 是什么? WebSocket 是一种网络通信协议.RFC6455 定义了它的通信标准. WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议 ...

  5. Swagger2的使用及注意事项

    一.Swagger的主要作用有两方面: 1.生成在线文档,通过注解方式生成在线文档,方便在定义修正接口时直接修改接口文档: 2.对接口文档在线测试,不用在输入接口地址以及里面的参数对象,可以很方便的对 ...

  6. 20165308 《Java程序设计》第9周学习总结

    20165308 <Java程序设计>第9周学习总结 教材学习内容总结 13章知识总结 获取地址 1.获取Internet上主机的地址 可以使用InetAddress类的静态方法getBy ...

  7. 运行成功的Demo(Python+Appium)

    原文摘自:廖丹  http://www.cnblogs.com/android-it/p/8805659.html 1.打开Appium运行 2.在Pycharm输入代码如下所示: from appi ...

  8. TCP加速锐速SS(ServerSpeeder)破解版一键安装

    速(serverspeeder),是一款TCP加速程序,能够增强VPS/服务器连接的稳定性,且有效的提高服务器的带宽利用率,进而提高访问速度.老左经常看到论坛.群里有用户提到锐速这款软件可以提高VPS ...

  9. 基于redis的乐观锁实践

    redis真是一个分布式应用场景下的好东西,对于我们的应用设计,功劳大大的! 今天要研究的是基于redis的事务机制以及watch指令(CAS)实现乐观锁的过程. 所谓乐观锁,就是利用版本号比较机制, ...

  10. 关于SD-SDI,HD-SDI,3G-SDI行号的问题

    关于SD-SDI,HD-SDI,3G-SDI行号的问题 1.资料来源 2.行号的插入信号 SD-SDI mode的信号不需要行号 HD-SDI,3G-SDI mode的信号必须插入行号 3.edh的插 ...