DataSource
DataSource是连接connection的工厂,通过它来获取连接,它主要的方法就是getConnection,使用这个方法来获取连接
public interface DataSource extends CommonDataSource, Wrapper {
/**
* <p>Attempts to establish a connection with the data source that
* this {@code DataSource} object represents.
*
* @return a connection to the data source
* @exception SQLException if a database access error occurs
* @throws java.sql.SQLTimeoutException when the driver has determined that the
* timeout value specified by the {@code setLoginTimeout} method
* has been exceeded and has at least tried to cancel the
* current database connection attempt
*/
Connection getConnection() throws SQLException;
/**
* <p>Attempts to establish a connection with the data source that
* this {@code DataSource} object represents.
*
* @param username the database user on whose behalf the connection is
* being made
* @param password the user's password
* @return a connection to the data source
* @exception SQLException if a database access error occurs
* @throws java.sql.SQLTimeoutException when the driver has determined that the
* timeout value specified by the {@code setLoginTimeout} method
* has been exceeded and has at least tried to cancel the
* current database connection attempt
* @since 1.4
*/
Connection getConnection(String username, String password)
throws SQLException;
}
Mybatis提供了两个实现类:PooledDataSource,UnpooledDataSource
DataSourceFactory
那么DataSource是怎么产生的呢,Mybatis是通过工厂来生成的
看下DataSourceFactory哪些方法
public interface DataSourceFactory {
// 设置连接参数
void setProperties(Properties props);
// 获取DataSource对象
DataSource getDataSource();
}
Mybatis提供了两个实现类:PooledDataSourceFactory和UnpooledDataSourceFactory,其中PooledDataSourceFactory又是UnpooledDataSourceFactory的子类
这两个子类工厂分别用来生成上面提到的PooledDataSource和UnpooledDataSource
UnPooledDataSourceFactory
主要看下构造函数以及setProperties
构造函数
public UnpooledDataSourceFactory() {
this.dataSource = new UnpooledDataSource();
}
setProperties
@Override
public void setProperties(Properties properties) {
Properties driverProperties = new Properties();
// 使用MetaObject来包裹datasource,目的是方便设置配置
MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
// 遍历配置,将配置设置到datasource中
for (Object key : properties.keySet()) {
String propertyName = (String) key;
// DRIVER_PROPERTY_PREFIX = driver.
// 如果配置是以driver开头的,那么将配置设置到driverProperties中
if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
String value = properties.getProperty(propertyName);
driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
} else if (metaDataSource.hasSetter(propertyName)) {
// 其他的属性会设置到datasource中
String value = (String) properties.get(propertyName);
// 这里会根据setter的参数类型,来将指定的配置值进行类型转换
Object convertedValue = convertValue(metaDataSource, propertyName, value);
metaDataSource.setValue(propertyName, convertedValue);
} else {
throw new DataSourceException("Unknown DataSource property: " + propertyName);
}
}
// 将driverProperties设置到datasource中
if (driverProperties.size() > 0) {
metaDataSource.setValue("driverProperties", driverProperties);
}
}
PooledDataSourceFactory
PooledDataSourceFactory继承了UnpooledDataSourceFactory,仅改动了构造函数
UnpooledDataSourceFactory使用的是UnpooledDataSource,PooledDataSourceFactory返回的是PooledDataSource
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
UnpooledDataSource
这里主要看下静态代码块以及getConnection方法
静态代码块
静态代码块
这里主要做的就是通过spi将实现了Driver接口的实现类都加载并且实例化,然后将这些实例化后的驱动实例注册注册到当前DataSource中
static {
// 遍历已经注册了的驱动,将这些驱动类复制到rigisterDrivers中
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
registeredDrivers.put(driver.getClass().getName(), driver);
}
}
当DriverManager类进行加载时,会执行它的静态代码块,其中会根据传入的driver配置,来加载指定的driver类
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
String drivers;
try {
// 获取当前的jdbc.drivers这个配置的值,其代表了当前需要加载的驱动类名
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 使用spi发现当前实现了Driver接口的服务提供者
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
// 当遍历这些实现了Driver接口的服务提供者之后,会加载对应的服务提供者,并且实例化,将对应的实例添加到map中
// Driver接口的实现类在加载的时候会执行静态代码块,从而将自己注册到DriverManager中
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
// 获取配置中配置的驱动类,然后进行加载
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
getConnection
这里可以看到每次获取连接的时候,都会重新建立一个连接,并不会复用
@Override
public Connection getConnection(String username, String password) throws SQLException {
return doGetConnection(username, password);
}
private Connection doGetConnection(String username, String password) throws SQLException {
Properties props = new Properties();
// 如果driver配置不为空,会设置到当前配置中,以driver.开头的配置会被识别为driver配置
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 {
initializeDriver();
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}
初始化驱动
private synchronized void initializeDriver() throws SQLException {
// 判断当前通过spi注册进来的Driver实现类实例中是否包含当前数据源使用的驱动
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
// 不存在驱动,会尝试进行加载
try {
if (driverClassLoader != null) {
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
// 将加载的驱动实例注册到DriverManager以及当前类的registeredDrivers中
Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}
获取连接
这里主要就是遍历当前已经注册的驱动类,然后使用这些驱动类的实例来获取连接
public static Connection getConnection(String url,
java.util.Properties info) throws SQLException {
return (getConnection(url, info, Reflection.getCallerClass()));
}
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
// 遍历当前已经注册的驱动类的实例
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
// 如果类加载器不能加载对应的驱动类,那么会直接跳过
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
// 尝试使用当前遍历的驱动实例来获取连接
Connection con = aDriver.driver.connect(url, info);
// 如果成功获取连接,那么直接返回
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
配置连接
这里会设置连接的网络超时时间、是否自动提交以及事务隔离级别
private void configureConnection(Connection conn) throws SQLException {
if (defaultNetworkTimeout != null) {
conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
}
if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
conn.setAutoCommit(autoCommit);
}
if (defaultTransactionIsolationLevel != null) {
conn.setTransactionIsolation(defaultTransactionIsolationLevel);
}
}
PooledDataSource
PooledDataSource相较于之前分写的UnpooledDataSource,最大的区别就是使用了池化技术,从而使连接可以复用,从而避免了频繁地创建和销毁连接
这里主要看下getConnection
getConnection
@Override
public Connection getConnection(String username, String password) throws SQLException {
return popConnection(username, password).getProxyConnection();
}
PoolState
这里首先看代表连接池状态的PoolState
// 当前管理的池化数据源对象
protected PooledDataSource dataSource;
// 当前空闲的连接
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;
popConnection
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;
while (conn == null) {
// 以同步的方式来获取连接
synchronized (state) {
// 当前存在空闲的连接
if (!state.idleConnections.isEmpty()) {
// 获取一个空闲连接
conn = state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
// 当前不存在空闲连接
// 在用连接数小于配置的最大连接数
if (state.activeConnections.size() < poolMaximumActiveConnections) {
// Can create new connection
// 通过数据源创建一个新的连接,这里的数据源是UnpooledDatasoure类型
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());
// 将之前包裹过期连接的PooledConnection的状态设置为失效
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();
// 开始等待最长poolTimeToWait时间
// 然后开始新的循环,重新走上面的流程尝试获取连接
state.wait(poolTimeToWait);
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
break;
}
}
}
}
// 当前成功获取到了连接
if (conn != null) {
//
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);
// 请求次数加一
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.");
}
}
}
}
}
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.");
}
return conn;
}
总结下上面的流程:
- 首先会尝试从空闲连接链表中获取连接,如果有空闲连接,就直接返回这个连接
- 如果没有空闲连接,那么会判断在连接是否有检出时间过长的,如果有,并且这个连接设置了自动提交,那么会先进行回滚,然后重新创建一个连接来包裹这个过期连接的真实连接
- 如果检出时间没有超过阈值的,那么会休眠一段时间,然后重新执行上面的步骤
PooledConnection
PooledDataSource并不会直接操作connection,而是会直接操作PooledConnection,PooledConnection是底层connection的代理类,它实现了InvocationHandler接口,下面主要看下invoke方法
invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
// 对connection的close方法进行特殊处理,这里会判断当前的空闲连接数是否已经达到上限,如果没有达到上限,那么会将当前连接加入到空闲连接中
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
// 当执行的是其他和connection相关的方法时,会判断当前连接的状态是否合法,如果不合法会报错
checkConnection();
}
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
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();
}
// 重新创建一个PooledConnection,底层使用的连接就是这次释放出来的连接,加入到空闲连接链表中
PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
state.idleConnections.add(newConn);
newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
// 将旧的连接对象无效化,当使用无效连接对象调用和连接相关的方法时,会报错
conn.invalidate();
if (log.isDebugEnabled()) {
log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
}
// 唤醒其他阻塞线程
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++;
}
}
}