Java 数据源类图
简单说明
CommonDataSource:是对数据源概念的顶层抽象,约束了数据源必需实现的方法。
从架构图中可以看出数据源有三种类型,即CommonDataSource抽象有三种实现方式,分别是:
DataSource、XADataSource、ConnectionPoolDataSource,三种类型数据源说明如下:
DataSource:基础实现,数据库物理连接的工厂,用于创建标准的数据库物理连接Connection,JDBC 2.0中诞生,与早先的DriverManager用途一样,新版本JDBC编程已推荐使用DataSource;
XADataSource:分布式事务实现,为支持分布式事务而诞生,此数据源直接生产出的不是数据库物理连接Connection,而是一个支持XA的XAConnection对象,
XAConnection对象可以直接生产数据库物理连接Connection,同时生产XAResource用于支持XA事务,通常XAConnection对象生产出的数据库物理连接Connection需要和该XAConnection生产出的XAResource对象配合使用以完成XA事务处理(请参考 XA 和 JTA 规范);
ConnectionPoolDataSource:连接池实现,此数据源实现并不直接创建数据库物理连接,而是一个逻辑实现,它的作用在于池化数据库物理连接,由于数据库物理连接是一个重量级的对象,频繁的创建销毁很影响性能,将物理连接池化后可降低创建和销毁的频率,复用连接以充分利用连接资源,此数据源通常不为用户应用所知,通常是由中间件服务方来调度,中间件服务方通过它获取一个池化对象PooledConnection,再通过该PooledConnection间接获取到物理连接,获取方式即是调用其getConnection()方法,
然后创建出一个规范定义的Connection接口句柄提供给应用使用,应用通过该句柄间接使用物理连接,在应用调用句柄的close方法时,中间件服务方的实现并没有真正调用物理连接的关闭,而是将其归还到连接池中。
另外,从类结构上可以看到XAConnection继承自PooledConnection,本身即是一个受管连接,可能设计之初也是考虑XA连接的池化机制了吧,也许是想管理普通Connection的池化对象PooledConnection可以兼容管理XAConnection(继承关系嘛),值得一提的是,在tomcat7的jdbc连接池的设计中,java自己的ConnectionPoolDataSource和PooledConnection机制已经被忽略掉了,tomcat7的jdbc连接池对外开发的连接句柄表象见如下代码片段:
org.apache.tomcat.jdbc.pool.ConnectionPool
Class<?> proxyClass = xa ?
Proxy.getProxyClass(ConnectionPool.class.getClassLoader(), new Class[] {java.sql.Connection.class,javax.sql.PooledConnection.class, javax.sql.XAConnection.class}) :
Proxy.getProxyClass(ConnectionPool.class.getClassLoader(), new Class[] {java.sql.Connection.class,javax.sql.PooledConnection.class});
proxyClassConstructor = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
Connection connection = null;
if (getPoolProperties().getUseDisposableConnectionFacade() ) {
connection = (Connection)proxyClassConstructor.newInstance(new Object[] { new DisposableConnectionFacade(handler) });
} else {
connection = (Connection)proxyClassConstructor.newInstance(new Object[] {handler});
}
//return the connection
return connection;
上文中动态生成的类定义是兼容PooledConnection的,但是在其代理的实现上却被无视了,代理是实现就是那个 handler,是一个InvocationHandler(实现类org.apache.tomcat.jdbc.pool.ProxyConnection),其invoke方法内容为:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (compare(ISCLOSED_VAL,method)) {
return Boolean.valueOf(isClosed());
}
if (compare(CLOSE_VAL,method)) {
if (connection==null) return null; //noop for already closed.
PooledConnection poolc = this.connection;
this.connection = null;
pool.returnConnection(poolc);
return null;
} else if (compare(TOSTRING_VAL,method)) {
return this.toString();
} else if (compare(GETCONNECTION_VAL,method) && connection!=null) {
return connection.getConnection();
} else if (method.getDeclaringClass().equals(XAConnection.class)) {
try {
return method.invoke(connection.getXAConnection(),args);
}catch (Throwable t) {
if (t instanceof InvocationTargetException) {
throw t.getCause() != null ? t.getCause() : t;
} else {
throw t;
}
}
}
if (isClosed()) throw new SQLException("Connection has already been closed.");
if (compare(UNWRAP_VAL,method)) {
return unwrap((Class<?>)args[0]);
} else if (compare(ISWRAPPERFOR_VAL,method)) {
return Boolean.valueOf(this.isWrapperFor((Class<?>)args[0]));
}
try {
PooledConnection poolc = connection;
if (poolc!=null) {
return method.invoke(poolc.getConnection(),args);
} else {
throw new SQLException("Connection has already been closed.");
}
}catch (Throwable t) {
if (t instanceof InvocationTargetException) {
throw t.getCause() != null ? t.getCause() : t;
} else {
throw t;
}
}
}
阅读可知,此invoke方法,只是将调用对象当作了Connection或XAConnection来处理,而并未考虑PooledConnection,所以如果应用将拿到的Connection句柄强制转换(动态定义时他确实是PooledConnection的实现,所以可强转)为PooledConnection时,调用PooledConnection特定的方法(如addConnectionEventListener/removeConnectionEventListener)就会出错,不过通常来讲应用也不会关心这些方法,应用也就只关心Connection和XAConnection。
tomcat连接池的实现在源码中是模块jdbc-pool,最后打包为 tomcat -jdbc.jar,有兴趣的可以研究下。