PageHelper Reentrantlock鎖使用問題
阿新 • • 發佈:2018-12-23
Mybatis-PageHelper應該是目前使用比較廣泛的一個Mybatis分頁外掛。我在幾個專案裡都引入了該外掛。今天偶然閱讀其原始碼,卻發現了一個不小的問題。
注:我閱讀的是最新的4.1.3的原始碼。
該分頁外掛的核心是PageHelper類,該類實現了Mybatis提供的Interceptor介面。介面定義的intercept方法實現如下:
/**
* Mybatis攔截器方法
*
* @param invocation 攔截器入參
* @return 返回執行結果
* @throws Throwable 丟擲異常
*/
public Object intercept (Invocation invocation) throws Throwable {
if (autoRuntimeDialect) {
SqlUtil sqlUtil = getSqlUtil(invocation);
return sqlUtil.processPage(invocation);
} else {
if (autoDialect) {
initSqlUtil(invocation);
}
return sqlUtil.processPage(invocation );
}
}
其中,getSqlUtil方法,實現如下:
/**
* 根據daatsource建立對應的sqlUtil
*
* @param invocation
*/
public SqlUtil getSqlUtil(Invocation invocation) {
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
//改為對dataSource做快取
DataSource dataSource = ms.getConfiguration().getEnvironment(). getDataSource();
String url = getUrl(dataSource);
if (urlSqlUtilMap.containsKey(url)) {
return urlSqlUtilMap.get(url);
}
ReentrantLock lock = new ReentrantLock(); //這裡我認為是有問題的。
try {
lock.lock();
if (urlSqlUtilMap.containsKey(url)) {
return urlSqlUtilMap.get(url);
}
if (StringUtil.isEmpty(url)) {
throw new RuntimeException("無法自動獲取jdbcUrl,請在分頁外掛中配置dialect引數!");
}
String dialect = Dialect.fromJdbcUrl(url);
if (dialect == null) {
throw new RuntimeException("無法自動獲取資料庫型別,請通過dialect引數指定!");
}
SqlUtil sqlUtil = new SqlUtil(dialect);
if (this.properties != null) {
sqlUtil.setProperties(properties);
} else if (this.sqlUtilConfig != null) {
sqlUtil.setSqlUtilConfig(this.sqlUtilConfig);
}
urlSqlUtilMap.put(url, sqlUtil);
return sqlUtil;
} finally {
lock.unlock();
}
}
這裡有個加鎖的操作,使用的是Reentrantlock區域性變數。這裡是有問題的,區域性變數的lock是無法起到鎖的作用的。這段程式碼實際上仍不是執行緒安全的。將該lock變數提升為類的全域性變數即可解決。
private ReentrantLock lock = new ReentrantLock();
/**
* 根據datasource建立對應的sqlUtil
*
* @param invocation
*/
public SqlUtil getSqlUtil(Invocation invocation) {
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
//改為對dataSource做快取
DataSource dataSource = ms.getConfiguration().getEnvironment().getDataSource();
String url = getUrl(dataSource);
if (urlSqlUtilMap.containsKey(url)) {
return urlSqlUtilMap.get(url);
}
try {
lock.lock();
...
順便也修改了一個datasource拼寫錯誤的問題。
給作者提交了Pull Request。