今天看了部分datasource包的内容,主要看了JndiDataSourceFactory类和PooledConnection类,学习部分总结
1、JndiDataSourceFactory
实现DataSourceFactory接口,主要有两个方法setProperties(设置属性), getDataSource()获取数据源
1.2、源码
package org.apache.ibatis.datasource.jndi;
import java.util.Map.Entry;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;
/**jndi(java naming directory interface) java命名目录接口,自己理解就是去某个地方获取配置信息,
* 然后通过配置信息创建数据源对象,这个规范就是为了解耦,就是不改java代码,只改配置文件
*
* @author Clinton Begin
*/
public class JndiDataSourceFactory implements DataSourceFactory {
/**
* 初始化上下文
*/
public static final String INITIAL_CONTEXT = "initial_context";
/**
* 数据源
*/
public static final String DATA_SOURCE = "data_source";
/**
* 环境前缀
*/
public static final String ENV_PREFIX = "env.";
/**
* 数据源
*/
private DataSource dataSource;
@Override
public void setProperties(Properties properties) {
try {
// 获取env.开头键值对的属性
// 初始化InitialContext对象
// Properties是Hashtable的子类
InitialContext initCtx;
Properties env = getEnvProperties(properties);
if (env == null) {
initCtx = new InitialContext();
} else {
initCtx = new InitialContext(env);
}
if (properties.containsKey(INITIAL_CONTEXT)
&& properties.containsKey(DATA_SOURCE)) {
Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
} else if (properties.containsKey(DATA_SOURCE)) {
dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
}
} catch (NamingException e) {
throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
}
}
@Override
public DataSource getDataSource() {
return dataSource;
}
/**
* 这个方法就是获取以env.开头的键值对
* @param allProps
* @return
*/
private static Properties getEnvProperties(Properties allProps) {
final String PREFIX = ENV_PREFIX;
Properties contextProperties = null;
for (Entry<Object, Object> entry : allProps.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (key.startsWith(PREFIX)) {
if (contextProperties == null) {
contextProperties = new Properties();
}
contextProperties.put(key.substring(PREFIX.length()), value);
}
}
return contextProperties;
}
}
- InitContext这个上下文对象需要好好看一下java 源码
2、PooledConnection(连接池对象)
2.1、源码
package org.apache.ibatis.datasource.pooled;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.ibatis.reflection.ExceptionUtil;
/**
* 连接池
* 1、连接池数据源、真实连接和代理连接对象
* 2、还有时间相关,比如上一次使用连接对象的时间
* 3、实现InvocationHandler的invoke方法,这个方法就是在调用真实方法之前调用该方法,在创建代理对象时候将代理对象与InvocationHandler
* 进行关联,相当于类成员变量proxyConnection,代理对象关联本类的invoke方法,主要是为判断是否执行是close方法,执行
* close方法需要进行额外的操作
* @author Clinton Begin
*/
class PooledConnection implements InvocationHandler {
/**
* 关闭
*/
private static final String CLOSE = "close";
/**
* 连接类,创建代理对象用改的
*/
private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };
/**
* hashCode
*/
private final int hashCode;
/**
* 连接池数据源
*/
private final PooledDataSource dataSource;
/**
* 真实连接
*/
private final Connection realConnection;
/**
* 代理连接
*/
private final Connection proxyConnection;
/**
* 检出时间戳
*/
private long checkoutTimestamp;
/**
* 创建的时间戳
*/
private long createdTimestamp;
/**
* 最后使用的时间戳
*/
private long lastUsedTimestamp;
/**
* 连接类型code, 干啥用的?
*/
private int connectionTypeCode;
/**
* 是否有效 valid?
*/
private boolean valid;
/**
* 使用连接对象和连接池数据源对象
* Constructor for SimplePooledConnection that uses the Connection and PooledDataSource passed in.
*
* @param connection - the connection that is to be presented as a pooled connection
* @param dataSource - the dataSource that the connection is from
*/
public PooledConnection(Connection connection, PooledDataSource dataSource) {
//连接hashCode
//连接对象
//数据源
//创建时间戳
//最后使用时间戳
//初始化有效
//代理连接对象创建在执行proxyConnection的方法将会调用 当前invoke的方法
this.hashCode = connection.hashCode();
this.realConnection = connection;
this.dataSource = dataSource;
this.createdTimestamp = System.currentTimeMillis();
this.lastUsedTimestamp = System.currentTimeMillis();
this.valid = true;
this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
}
/**
* 设置无效连接
* Invalidates the connection.
*/
public void invalidate() {
valid = false;
}
/**
* 判断是否有效, 本身字段状态是有效的,同时真实连接是否不为null, 数据源是否可以ping通过
* Method to see if the connection is usable.
*
* @return True if the connection is usable
*/
public boolean isValid() {
return valid && realConnection != null && dataSource.pingConnection(this);
}
/**
* 获取真实连接
* Getter for the *real* connection that this wraps.
*
* @return The connection
*/
public Connection getRealConnection() {
return realConnection;
}
/**
* 获取代理连接
* Getter for the proxy for the connection.
*
* @return The proxy
*/
public Connection getProxyConnection() {
return proxyConnection;
}
/**
* 或真正连接的hash值,如果null 返回0
* Gets the hashcode of the real connection (or 0 if it is null).
*
* @return The hashcode of the real connection (or 0 if it is null)
*/
public int getRealHashCode() {
return realConnection == null ? 0 : realConnection.hashCode();
}
/**
* 基于url用户名、密码来获取连接类型code
* Getter for the connection type (based on url + user + password).
*
* @return The connection type
*/
public int getConnectionTypeCode() {
return connectionTypeCode;
}
/**
* 设置连接类型
* Setter for the connection type.
*
* @param connectionTypeCode - the connection type
*/
public void setConnectionTypeCode(int connectionTypeCode) {
this.connectionTypeCode = connectionTypeCode;
}
/**
* 获取当前时间戳
* Getter for the time that the connection was created.
*
* @return The creation timestamp
*/
public long getCreatedTimestamp() {
return createdTimestamp;
}
/**
* 设置创建的时间戳
* Setter for the time that the connection was created.
*
* @param createdTimestamp - the timestamp
*/
public void setCreatedTimestamp(long createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
/**
* 获取最后时间戳
* Getter for the time that the connection was last used.
*
* @return - the timestamp
*/
public long getLastUsedTimestamp() {
return lastUsedTimestamp;
}
/**
* 设置最后使用的时间戳
* Setter for the time that the connection was last used.
*
* @param lastUsedTimestamp - the timestamp
*/
public void setLastUsedTimestamp(long lastUsedTimestamp) {
this.lastUsedTimestamp = lastUsedTimestamp;
}
/**
* 获取上次使用时间到现在这段时间间隔
* Getter for the time since this connection was last used.
*
* @return - the time since the last use
*/
public long getTimeElapsedSinceLastUse() {
return System.currentTimeMillis() - lastUsedTimestamp;
}
/**
* 连接对象年龄,也就是从创建开始现在时间,可以把连接对象当做一个生物出现
* Getter for the age of the connection.
*
* @return the age
*/
public long getAge() {
return System.currentTimeMillis() - createdTimestamp;
}
/**
* 获取这个连接对象的检出的时间戳
* Getter for the timestamp that this connection was checked out.
*
* @return the timestamp
*/
public long getCheckoutTimestamp() {
return checkoutTimestamp;
}
/**
* 设置检出的时间戳
* Setter for the timestamp that this connection was checked out.
*
* @param timestamp the timestamp
*/
public void setCheckoutTimestamp(long timestamp) {
this.checkoutTimestamp = timestamp;
}
/**
* 获取检出的时间
* Getter for the time that this connection has been checked out.
*
* @return the time
*/
public long getCheckoutTime() {
return System.currentTimeMillis() - checkoutTimestamp;
}
/**
* 重写hash方法
* @return
*/
@Override
public int hashCode() {
return hashCode;
}
/**
* 判断两个连接对象是否相等,
* 两个真是的连接对象的hashCode是否相等,他们本身的hashcode是否相等
* Allows comparing this connection to another.
*
* @param obj - the other connection to test for equality
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof PooledConnection) {
return realConnection.hashCode() == ((PooledConnection) obj).realConnection.hashCode();
} else if (obj instanceof Connection) {
return hashCode == obj.hashCode();
} else {
return false;
}
}
/**
* Required for InvocationHandler implementation.
* 实现InvocationHandler的实现方法, 代理对象是为了,判断方法是不是close方法
* @param proxy - not used
* @param method - the method to be executed
* @param args - the parameters to be passed to the method
* @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取Connection调用的方法的名称是不是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);
}
}
/**
* 检查连接是否有效
* @throws SQLException
*/
private void checkConnection() throws SQLException {
if (!valid) {
throw new SQLException("Error accessing PooledConnection. Connection is invalid.");
}
}
}