今天看了部分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.");
}
}
}