上一篇文章,还剩 environments对数据源解析,事务配置解析
//1.2.4
private void environmentsElement(XNode context) throws Exception {
/**
* environments节点标签不为空才解析,
* 我们也可以不在 mybatis-configuration.xml 文件中配置 标签,交给spring管理
*/
if (context != null) {
//如果 environment 值为 null,
if (environment == null) {
//获取中的default属性值
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
/**
* 循环遍历子标签
*/
//获取中的id属性值
String id = child.getStringAttribute("id");
//遍历所有的时候一次判断相应的id是否是default设置的值
if (isSpecifiedEnvironment(id)) {
//1.2.4.1 获取配置的事务管理器
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//1.2.4.2 构建通过type对应的DataSourceFactory
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
//1.2.4.3 通过type对应的类型的DataSourceFactory构建DataSource
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
private boolean isSpecifiedEnvironment(String id) {
if (environment == null) {
throw new BuilderException("No environment specified.");
} else if (id == null) {
throw new BuilderException("Environment requires an id attribute.");
} else if (environment.equals(id)) {
return true;
}
return false;
}
/**
* 1.2.4.2 构建DataSourceFactory
*
* @param context
* @return
* @throws Exception
*/
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
//根据type,创建不同的DataSourceFactory
String type = context.getStringAttribute("type");
//解析数据库连接信息,保存至props(Properties继承Hashtable)
Properties props = context.getChildrenAsProperties();
//通过type类型,构建不同类型的DataSourceFactory(工厂模式)
//通过type(别名)从Map> typeAliases = new HashMap<>();获取对应的DataSourceFactory
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
//将数据库连接信息的值设置到DataSourceFactory
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
1.2.4.3 通过type对应的类型的DataSourceFactory构建DataSource
通过type构建不同的DataSourceFactory ,构建对应的DataSource
PooledDataSourceFactory--> PooledDataSource
UnpooledDataSourceFactory--> UnpooledDataSource
不同类型的DataSource在获取连接时的方式不一
UnPooledDataSource 的 getConnection() 方法实现:
public Connection getConnection() throws SQLException {
return doGetConnection(username, password);
}
public Connection getConnection(String username, String password) throws SQLException {
return doGetConnection(username, password);
}
private Connection doGetConnection(String username, String password) throws SQLException {
//将数据连接信息、驱动都封装到Properties文件中
Properties props = new Properties();
if (driverProperties != null) {
props.putAll(driverProperties);
}
if (username != null) {
props.setProperty("user", username);
}
if (password != null) {
props.setProperty("password", password);
}
return doGetConnection(props);
}
/**
* 获取数据库连接
*/
private Connection doGetConnection(Properties properties) throws SQLException {
//1、初始化驱动
initializeDriver();
//2、从DriverManager中获取连接,获取新的Connection对象
Connection connection = DriverManager.getConnection(url, properties);
//3、配置connection属性
configureConnection(connection);
return connection;
}
使用 UnpooledDataSource 类型的数据源,每次需要连接的时候都会调用 getConnection() 即每次都需要经过以下步骤。在底层就相当于和数据库建立的通信连接,在建立通信连接的过程会消耗一些资源
//1、初始化驱动
initializeDriver();
//2、从DriverManager中获取连接,获取新的Connection对象
Connection connection = DriverManager.getConnection(url, properties);
//3、配置connection属性
configureConnection(connection);
return connection;
//空闲状态的连接集合
protected final List idleConnections = new ArrayList<>();
//活动状态的连接集合
protected final List activeConnections = new ArrayList<>();
PooledDataSource将java.sql.Connection对象包裹成PooledConnection对象放到了PoolState类型的容器中维护。 PooledConnection的 空闲状态(idle)和活动状态(active),分别被存储到PoolState容器内的idleConnections和activeConnections两个List集合中。
调用PooledDataSource的getConnection()方法时,会优先从idleConnections集合中取PooledConnection对象,如果没有,则activeConnections集合是否已满,如果未满,PooledDataSource会创建出一个PooledConnection,添加到此集合中,并返回。
使用动态代理,真正的Connection对象创建一个代理对象,代理对象所有的方法都是调用相应的真正Connection对象的方法实现。当代理对象执行close()方法时,要特殊处理,不调用真正Connection对象的close()方法,而是将Connection对象添加到连接池中。。
在调用con.close()方法的时候,不真正调用close()方法,将其换成将Connection对象放到连接池容器中的代码!
class PooledConnection implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理调用close方法。
String methodName = method.getName();
if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
//从活动状态的连接集合中移除,添加到空闲状态的集合中
dataSource.pushConnection(this);
return null;
}
try {
if (!Object.class.equals(method.getDeclaringClass())) {
// issue #579 toString() should never fail
// throw an SQLException instead of a Runtime
checkConnection();
}
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}