数据源创建和和策略模式
一、数据源
- 在10-Mybatis源码和设计模式-1(数据源模块和工厂模式,代理模式)中我们了解了数据源模块有三种类型,POOL,UNPOOL和JNDI三种,知道这三种类型的数据源都是通过工厂模式创建出来的,但没有分析数据源的创建过程和创建的策略,仅仅只是静态分析了源码结构。数据源的类型由Environment里的type指定,对于客户端来说不管底层是如何创建数据源的都没有关系,只需要修改配置就能够生产出3种不同类型的数据源。而这种思想恰好和策略模式相符合,因此数据源的创建使用了策略模式。
- 关于策略模式,可以参考: 03-行为型模式(上)
1.1 配置
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc_driver}"/>
<property name="url" value="${jdbc_url}"/>
<property name="username" value="${jdbc_username}"/>
<property name="password" value="${jdbc_password}"/>
</dataSource>
</environment>
<environment id="other">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc_driver_other}"/>
<property name="url" value="${jdbc_url_other}"/>
<property name="username" value="${jdbc_username_other}"/>
<property name="password" value="${jdbc_password_other}"/>
</dataSource>
</environment>
</environments>
1.2 源码
- 这小节我们通过跟踪源码来看数据源模块的创建过程,在文章 16-Mybatis 核心流程01-初始化阶段中我分析了Mybatis初始化过程,这个过程中会解析Mybatis的配置文件生产Configuration对象,而数据源类型的配置是在主配置文件的environments节点,由此容易推测数据源的创建应该是在environments节点解析的入口里面。我直接定位到XMLConfigBuilder#parseConfiguration方法,该方法是解析的主流程,而XMLConfigBuilder#environmentsElement就是解析environments节点的入口,也是我们想要找的。
1.2.1 XMLConfigBuilder#environmentsElement
- XMLConfigBuilder#environmentsElement是解析主配置文件environments节点的方法
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
1.2.2 XMLConfigBuilder#dataSourceElement
- XMLConfigBuilder#dataSourceElement是解析主配置文件dataSource子节点的方法。
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) {
return null;
}
String key = string.toLowerCase(Locale.ENGLISH);
Class<T> value;
if (TYPE_ALIASES.containsKey(key)) {
value = (Class<T>) TYPE_ALIASES.get(key);
} else {
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
- 下面是我调试的时候,查看到的TypeAliasRegistry.TYPE_ALIASES这个map里保存的别名和类的对应关系:
![image](https://note.youdao.com/yws/api/personal/file/F14EC97E9EBB4D4F818EA09355011D90?method=download&shareKey=a34abfc7c08a3ffbc88ad5224b234502)
- 到此我们看到了数据源的创建过程 : 获取配置->策略模式实例化工厂->获取数据源->构建Environment;
详细步骤如下所示。
->解析environments节点
->获取default表示的id
->找到对应id的environment配置
->获取transactionManager配置
->获取driver,url,username,password配置
->根据别名找到对应的工厂类,实例化工厂类->设置属性给工厂类
->从工厂获取数据源dataSource
->builder模式创建Environment,传入transactionManager和dataSource参数构造Environment对象
->将Environment对象设置到Configuration对象
二、小结