MyBatis之配置文件解析_数据库联接池
数据源工厂接口及实现类
Mybatis定义了接口DataSourceFactory,可用来创建DataSource对象,这个接口很简单,只定义了两个方法。
public interface DataSourceFactory {
// 提供属性对象供数据源对象使用
void setProperties(Properties props);
// 获得DataSource对象
DataSource getDataSource();
}
Mybatis提供了DataSourceFactory的实现类:UnpooledDataSourceFactory、PooledDataSourceFactory及JndiDataSourceFactory。其中PooledDataSourceFactory是UnpooledDataSourceFactory的子类,UnpooledDataSourceFactory创建的DataSource是UnpooledDataSource对象,PooledDataSourceFactory创建的DataSource是PooledDataSource。PooledDataSource是我们重点分析的对象
配置文件中数据源的配置
数据源的配置标签是dataSource,其属性type是一个DataSourceFactory实现类的别名,例子中的别名是POOLED,即PooledDataSourceFactory,这个别名在Configuration构造方法中注册。
<!-- mybatis环境相关配置 -->
<environments default="development">
<environment id="development">
<!-- 事务工厂的类型, JDBC是mybatis提供的 -->
<transactionManager type="JDBC"/>
<!-- 数据源类型, POOLED是mybatis提供的,配置的类型是DataSourceFactory -->
<dataSource type="POOLED">
<!-- 这种形式的配置,解析时转换为Properties对象, 创建DataSource对象,可以用这里的name-value,设置属性值 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${uname}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<!-- 可以配置多个环境 -->
</environments>
DataSourceFactory默认实现类别名的注册:
public Configuration() {
// DataSourceFactory默认实现类别名的注册
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
}
dataSource标签的解析
XMLConfigBuilder的方法dataSourceElement用来解析dataSource标签,该方法比较简单,即获得dataSource的属性type,同时获得dataSource标签下的所有property子标的集合,创建好DataSourceFactory对象,并设置属性对象。若没有配置dataSource标签,则抛出BuilderException异常。environmentsElement方法获得DataSourceFactory后,即可创建好DataSource对象,保存在Environment中,Environment持有一个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).getDeclaredConstructor().newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
UnpooledDataSource代码分析
UnPooledDataSource实现了DataSource接口,通过DriverManager获得Connection对象。
public class UnpooledDataSource implements DataSource {
private ClassLoader driverClassLoader;
private Properties driverProperties;
private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();
// JDBC联接数据库的四大参数,
private String driver;
private String url;
private String username;
private String password;
private Boolean autoCommit;
private Integer defaultTransactionIsolationLevel;
private Integer defaultNetworkTimeout;
// 提供多个构造方法获得UnPooledDataSource对象,PooledDataSource通过这个方法获得真正的联接对象
// 真正获得联接对象的方法
private Connection doGetConnection(Properties properties) throws SQLException {
// 初始化参数
initializeDriver();
// 获得Connection对象
Connection connection = DriverManager.getConnection(url, properties);
// 设置connection对象的属性,包插网络过期时间、是否自动提交、事务隔离级别
configureConnection(connection);
return connection;
}
}
PooledDataSource代码分析
PooledDataSource也实现了DataSource是Mybatis数据库联接池的实现,内部使用UnPooldDataSource获得联接对象
PooledDataSource有两个帮助类PoolState它与PooledDataSource是一对一关联,互为所有,主要描述池的状态,如有空闲联接的集合与活动联接的集合
PooledConnection是mybatis内部使用的一个类,这是联接池中存放的对象,实际返回的联接对象是Connection一个动态代理对象
public class PooledDataSource implements DataSource {
private static final Log log = LogFactory.getLog(PooledDataSource.class);
// 池的状态,
private final PoolState state = new PoolState(this);
// 所使用的UnpooledDataSource
private final UnpooledDataSource dataSource;
// OPTIONAL CONFIGURATION FIELDS,池的特性
protected int poolMaximumActiveConnections = 10; // 最大活动联接
protected int poolMaximumIdleConnections = 5; // 最大空闲联接
protected int poolMaximumCheckoutTime = 20000; // 最大
protected int poolTimeToWait = 20000; // 最大等待时间
protected int poolMaximumLocalBadConnectionTolerance = 3; // 最大本地坏联接
protected String poolPingQuery = "NO PING QUERY SET";
protected boolean poolPingEnabled;
protected int poolPingConnectionsNotUsedFor;
// 多个构造方法创建PooledDataSource对象,略
// 多个重载的getConnection方法,如,是从联接池中获得联接对象
@Override
public Connection getConnection() throws SQLException {
// 返回的是动态代理联接对象(Connection动态代理对象),在PooledConnection中定义
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}
// 从联接池中获得PooledConnection对象的核心方法,返回PooledConnection对象
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;
// 循环等待一直到有可用的PooledConnection对象
while (conn == null) {
synchronized (state) {
// 如果池中有空闲的联接,
if (!state.idleConnections.isEmpty()) {
// Pool has available connection,获得这个联接,并且从空闲池中去掉这个联接
conn = state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
// Pool does not have available connection,没有空闲的联接池对象
// 如果还没有达到最大活动数,则创建一个新的PooledConnection对象,
if (state.activeConnections.size() < poolMaximumActiveConnections) {
// Can create new connection,则创建一个新的PooledConnection对象,
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
// Cannot create new connection,如果不允许创建新的联接对象
// 取出活动联接中的第一个联接
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
// 检查这个联接是否过期,若已过期,则使用这个联接
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// Can claim overdue connection
state.claimedOverdueConnectionCount++;
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
state.accumulatedCheckoutTime += longestCheckoutTime;
// 则在活动联接中去掉这个联接
state.activeConnections.remove(oldestActiveConnection);
// 如果联接不是自动提交
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
// 回滚这个联接
oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException e) {
/*
Just log a message for debug and continue to execute the following
statement like nothing happened.
Wrap the bad connection with a new PooledConnection, this will help
to not interrupt current executing thread and give current thread a
chance to join the next competition for another valid/good database
connection. At the end of this loop, bad {@link @conn} will be set as null.
*/
// 如有异常只有记录一下
log.debug("Bad connection. Could not roll back");
}
}
// 基于原来的联接(真实的联接),创建一个新的PooledConnection对象
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
// Must wait,若没有找 到联接
try {
if (!countedWait) {
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
// 在state对象上等待释放出的联接,等待最长时间为poolTimeWait
state.wait(poolTimeToWait);
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
break;
}
}
}
}
// 已经获得联接对象
if (conn != null) {
// ping to server and check the connection is valid or not,测试这个联接是否有效
// 联接有效
if (conn.isValid()) {
if (!conn.getRealConnection().getAutoCommit()) {
// 若不是自动提交,则回滚原来的数据
conn.getRealConnection().rollback();
}
// 设置属性值
conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
conn.setCheckoutTimestamp(System.currentTimeMillis());
conn.setLastUsedTimestamp(System.currentTimeMillis());
// 将这个联接加到活动联接池中
state.activeConnections.add(conn);
// 联接请求数加1
state.requestCount++;
// 加累计请求时间
state.accumulatedRequestTime += System.currentTimeMillis() - t;
} else {// 联接无效
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
}
state.badConnectionCount++;
localBadConnectionCount++;
conn = null; // 重新获得联接
// 若坏的联接个数太多,则抛出异常
if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Could not get a good connection to the database.");
}
throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
}
}
}
}
}
// 现测试一下conn是否为空,若是则抛出异常
if (conn == null) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
// 返回PooledConnection对象
return conn;
}
}
// 将一个联接对象返回给联接池,当调用close方法时,通过动态代理对象,调用这个方法
protected void pushConnection(PooledConnection conn) throws SQLException {
synchronized (state) {
// 从活动联接池中去掉这个联接
state.activeConnections.remove(conn);
// 若联接有效
if (conn.isValid()) {
//
if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
state.accumulatedCheckoutTime += conn.getCheckoutTime();
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
// 基于原来的Connection对象,创建新的PooledConnection对象
PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
// 加入到空闲池中
state.idleConnections.add(newConn);
newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
// 让原来的conn失效
conn.invalidate();
if (log.isDebugEnabled()) {
log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
}
// 唤醒在state上等待的线程,有空闲的联接对象可用
state.notifyAll();
} else { //空闲池个数已满
state.accumulatedCheckoutTime += conn.getCheckoutTime();
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
// 关闭真正的联接
conn.getRealConnection().close();
if (log.isDebugEnabled()) {
log.debug("Closed connection " + conn.getRealHashCode() + ".");
}
// 让原来的联接无效
conn.invalidate();
}
} else { // 记录联接已无效
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
}
state.badConnectionCount++;
}
}
}
public class PoolState {
protected PooledDataSource dataSource;
// 这里存放的是PooedConnection对象,联接池的数据结构
// 空闲联接对象集合
protected final List<PooledConnection> idleConnections = new ArrayList<>();
// 空闲活动联接对象集合
protected final List<PooledConnection> activeConnections = new ArrayList<>();
protected long requestCount = 0;
protected long accumulatedRequestTime = 0;
protected long accumulatedCheckoutTime = 0;
protected long claimedOverdueConnectionCount = 0;
protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
protected long accumulatedWaitTime = 0;
protected long hadToWaitCount = 0;
protected long badConnectionCount = 0;
}
// 内部使用的类,实现InvocationHandler,实现了invoke方法
class PooledConnection implements InvocationHandler {
private static final String CLOSE = "close";
// 动态代理实现的接口
private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };
private final int hashCode;
private final PooledDataSource dataSource;
// 真正的联接对象
private final Connection realConnection;
// 代理联接对象,InvocationHandler即PooledConnection
private final Connection proxyConnection;
private long checkoutTimestamp;
private long createdTimestamp;
private long lastUsedTimestamp;
private int connectionTypeCode;
private boolean valid;
public PooledConnection(Connection connection, PooledDataSource dataSource) {
this.hashCode = connection.hashCode();
this.realConnection = connection;
this.dataSource = dataSource;
this.createdTimestamp = System.currentTimeMillis();
this.lastUsedTimestamp = System.currentTimeMillis();
this.valid = true;
// 关键代码为联接对象Connection创建了动态代理对象
this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
// 当调用Connection的close方法时,调用PooledDataSource的pushConnection方法
if (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();
}
// 调用原始Connection方法
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix