我先简单的给大家示例一下 properties 的使用方法.
- <configuration>
- <!-- 方法一: 从外部指定 properties 配置文件, 除了使用 resource 属性指定外, 还可通过 url 属性指定 url
- <properties resource="dbConfig.properties"></properties>
- -->
- <!-- 方法二: 直接配置为 xml -->
- <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"/>
- </properties>
属性也可以被传递到 SqlSessionFactoryBuilder.build() 方法中. 例如:
- SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
- // ... 或者 ...
- SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
如果属性在不只一个地方进行了配置, 那么 MyBatis 将按照下面的顺序来加载:
在 properties 元素体内指定的属性首先被读取.
然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件, 并覆盖已读取的同名属性.
最后读取作为方法参数传递的属性, 并覆盖已读取的同名属性.
再看一下 envirements 元素节点的使用方法吧:
- <environments default="development">
- <environment id="development">
- <transactionManager type="JDBC"/>
- <dataSource type="POOLED">
- <!-- 上面指定了数据库配置文件, 配置文件里面也是对应的这四个属性 -->
- <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>
每个数据库对应一个 SqlSessionFactory 实例
为了指定创建哪种环境, 只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可. 可以接受环境配置的两个方法签名是:
- SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
- SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
如果忽略了环境参数, 那么默认环境将会被加载, 如下所示:
默认使用的环境 ID(比如: default="development").
每个 environment 元素定义的环境 ID(比如: id="development").
事务管理器的配置 (比如: type="JDBC").
数据源的配置 (比如: type="POOLED").
事务管理器 (transactionManager)
在 MyBatis 中有两种类型的事务管理器 (也就是 type="[JDBC|MANAGED]"):
JDBC - 这个配置就是直接使用了 JDBC 的提交和回滚设置, 它依赖于从数据源得到的连接来管理事务作用域.
MANAGED - 这个配置几乎没做什么. 它从来不提交或回滚一个连接, 而是让容器来管理事务的整个生命周期 (比如 JEE 应用服务器的上下文). 默认情况下它会关闭连接, 然而一些容器并不希望这样, 因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为.
有三种内建的数据源类型 (type="[UNPOOLED|POOLED|JNDI]"):
UNPOOLED- 这个数据源的实现只是每次被请求时打开和关闭连接. 虽然有点慢, 但对于在数据库连接可用性方面没有太高要求的简单应用程序来说, 是一个很好的选择.
POOLED- 这种数据源的实现利用 "池" 的概念将 JDBC 连接对象组织起来, 避免了创建新的连接实例时所必需的初始化和认证时间. 这是一种使得并发 web 应用快速响应请求的流行处理方式.
JNDI - 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用, 容器可以集中或在外部配置数据源, 然后放置一个 JNDI 上下文的引用.
上次我们说过 mybatis 是通过 XMLConfigBuilder 这个类在解析 mybatis 配置文件的, 那么本次就接着看看 XMLConfigBuilder 对于 properties 和 environments 的解析:
- public class XMLConfigBuilder extends BaseBuilder {
- private boolean parsed;
- //xml 解析器
- private XPathParser parser;
- private String environment;
- // 上次说到这个方法是在解析 mybatis 配置文件中能配置的元素节点
- // 今天首先要看的就是 properties 节点和 environments 节点
- private void parseConfiguration(XNode root) {
- try {
- // 解析 properties 元素
- propertiesElement(root.evalNode("properties")); //issue #117 read properties first
- typeAliasesElement(root.evalNode("typeAliases"));
- pluginElement(root.evalNode("plugins"));
- objectFactoryElement(root.evalNode("objectFactory"));
- objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
- settingsElement(root.evalNode("settings"));
- // 解析 environments 元素
- environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
- 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) {
- // 将子节点的 name 以及 value 属性 set 进 properties 对象
- // 这儿可以注意一下顺序, xml 配置优先, 外部指定 properties 配置其次
- Properties defaults = context.getChildrenAsProperties();
- // 获取 properties 节点上 resource 属性的值
- String resource = context.getStringAttribute("resource");
- // 获取 properties 节点上 url 属性的值, resource 和 url 不能同时配置
- 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 文件 set 进 Properties 对象
- if (resource != null) {
- defaults.putAll(Resources.getResourceAsProperties(resource));
- } else if (url != null) {
- defaults.putAll(Resources.getUrlAsProperties(url));
- }
- // 将 configuration 对象中已配置的 Properties 属性与刚刚解析的融合
- //configuration 这个对象会装载所解析 mybatis 配置文件的所有节点元素, 以后也会频频提到这个对象
- // 既然 configuration 对象用有一系列的 get/set 方法, 那是否就标志着我们可以使用 java 代码直接配置?
- // 答案是肯定的, 不过使用配置文件进行配置, 优势不言而喻
- Properties vars = configuration.getVariables();
- if (vars != null) {
- defaults.putAll(vars);
- }
- // 把装有解析配置 propertis 对象 set 进解析器, 因为后面可能会用到
- parser.setVariables(defaults);
- //set 进 configuration 对象
- configuration.setVariables(defaults);
- }
- }
- // 下面再看看解析 enviroments 元素节点的方法
- private void environmentsElement(XNode context) throws Exception {
- if (context != null) {
- if (environment == null) {
- // 解析 environments 节点的 default 属性的值
- // 例如: <environments default="development">
- environment = context.getStringAttribute("default");
- }
- // 递归解析 environments 子节点
- for (XNode child : context.getChildren()) {
- //<environment id="development">, 只有 enviroment 节点有 id 属性, 那么这个属性有何作用?
- //environments 节点下可以拥有多个 environment 子节点
- // 类似于这样: <environments default="development"><environment id="development">...</environment><environment id="test">...</environments>
- // 意思就是我们可以对应多个环境, 比如开发环境, 测试环境等, 由 environments 的 default 属性去选择对应的 enviroment
- String id = child.getStringAttribute("id");
- //isSpecial 就是根据由 environments 的 default 属性去选择对应的 enviroment
- if (isSpecifiedEnvironment(id)) {
- // 事务, mybatis 有两种: JDBC 和 MANAGED, 配置为 JDBC 则直接使用 JDBC 的事务, 配置为 MANAGED 则是将事务托管给容器,
- TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
- //enviroment 节点下面就是 dataSource 节点了, 解析 dataSource 节点 (下面会贴出解析 dataSource 的具体方法)
- DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
- DataSource dataSource = dsFactory.getDataSource();
- Environment.Builder environmentBuilder = new Environment.Builder(id)
- .transactionFactory(txFactory)
- .dataSource(dataSource);
- // 老规矩, 会将 dataSource 设置进 configuration 对象
- configuration.setEnvironment(environmentBuilder.build());
- }
- }
- }
- }
- // 下面看看 dataSource 的解析方法
- private DataSourceFactory dataSourceElement(XNode context) throws Exception {
- if (context != null) {
- //dataSource 的连接池
- String type = context.getStringAttribute("type");
- // 子节点 name, value 属性 set 进一个 properties 对象
- Properties props = context.getChildrenAsProperties();
- // 创建 dataSourceFactory
- DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
- factory.setProperties(props);
- return factory;
- }
- throw new BuilderException("Environment declaration requires a DataSourceFactory.");
- }
- }
- View Code
通过以上对 mybatis 源码的解读, 相信大家对 mybatis 的配置又有了一个深入的认识.
还有一个问题, 上面我们看到, 在配置 dataSource 的时候使用了 ${driver} 这种表达式, 这种形式是怎么解析的? 其实, 是通过 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 + "}";
- }
- }
- }
来源: http://www.bubuko.com/infodetail-3103107.html