【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. linqpad使用方法备忘

    1.使用EF更新数据库 void Main() { select item).ToList(); CM_BookPages.DeleteAllOnSubmit(items); SubmitChange ...

  2. 黑马-Spring与数据库

    Spring与数据库 Spring与jdbc 引入dataSource 在客户端 模板编程 类的结构图, 真正干活的是JdbcTemplate(底层实现,操作 excute方法) JdbcTempla ...

  3. CSS Overrides: Friend or Foe?

    转自:http://www.callumhart.com/blog/css-overrides-friend-or-foe Anyone familiar with CSS will know how ...

  4. What are long running processes?

    转自:https://blog.bernd-ruecker.com/what-are-long-running-processes-b3ee769f0a27 Some communities have ...

  5. KMPlayer速度步进设置及快捷键设置和同级视频自动加入播放列表

    速度步进设置 快捷键设置  同级视频自动加入播放列表

  6. Navicat permium工具连接Oracle的配置

    目标数据库是Oracle11g R2 64位的 搜索很多说是需要32位的Oracle客户端,而且是要下载 Basic 版本,但是下载32位的Oracle客户端还是不行 最后把Navicat Premi ...

  7. mysql的变量信息详解

    mysql的变量详解 执行show variables命令可以查看MySQL服务器的变量 变量名 默认值 说明 对应的配置文件参数 auto_increment_increment 1 自增长类型的初 ...

  8. 纯css3实现文字间歇滚动效果

    场景: 假设有4条数据或者标题,视口中只显示两条,采用每次向上滚动一条数据来展示所有的数据.效果如图: 用JavaScript也很容易实现,但是需要操作DOM,可以参考这篇博客.考虑到项目中经常使用V ...

  9. 黄聪:PHP数据库连接失败--could not find driver 解决办法

    数据库连接失败could not find driver在调试一个PHP程序时,报了这个错误, could not find driver 经过一番查找,结合自己的思考和实践,终于找到了问题所在. 原 ...

  10. Ubuntu16.04 LTS软件中心闪退及修改阿里源

    现象: 进入软件中心点击任意,直接退出 解决办法: 先更换软件源,我的为阿里云 1. 备份 源位置 :/etc/apt/sources.list 2. 更改 sudo vi /etc/apt/sour ...