從myBatis Plugin看責任鏈模式
一、介紹
在mybatis
中從sql
的解析到最后結果集的返回,經過了一系列的內部組件,比如參數處理器parameterHandler
,語句處理器StatementHandler
,結果集處理器ResultSetHandler
等。若開發者需要對SQL
執行的某一環節進行一些特定的處理,比如參數類型的轉換,數據分頁功能,打印執行的SQL
語句等都可以通過mybatis
的插件機制實現。
二、mybatis的責任鏈
mybatis
中就是對內部的一個List
數組做攔截,業務方通過實現Interceptor
接口后,將具體的實現類通過InterceptorChain#addInterceptor
添加到責任鏈中,當mybatis
初始化資源時,會調用InterceptorChain#pluginAll
通過代理的方式,將所有的插件通過逐層代理的方式將內部核心組件(比如ParameterHandler
)包裹返回一個代理對象。
真正執行的地方是由於將內部核心組件都包裝成了代理類,所以在調用執行方法時,會被代理對象攔截進入invoke
方法,根據執行方法所屬類以及注解等判斷是否執行攔截器或者是執行原方法。
public interface Interceptor {
/**
* 攔截執行該方法
**/
Object intercept(Invocation invocation) throws Throwable;
/**
* 插入
**/
Object plugin(Object target);
/**
* 設置屬性
**/
void setProperties(Properties properties);
}
public class InterceptorChain {
/**
* 內部就是一個攔截器的List
*/
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
//循環調用每個Interceptor.plugin方法
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
三、過濾器相關責任鏈
在權限校驗等一些攔截器中,通常的做法是有多層攔截,比如簡單的登錄過程,先校驗用戶名密碼是否正確,在校驗是否擁有某項操作的操作權限之后才會使得用戶獲取到資源,但是如果用戶名密碼校驗失敗,就沒有必要進入第二部的操作權限校驗,所以這種場景下使用mybatis
那種方式的責任鏈有所不妥。以下是基於在多層攔截下,若某層校驗失敗,直接拒絕繼續往下校驗的責任鏈模式。
/**
* 攔截器接口
**/
public interface Filter {
/**
* 攔截執行方法
**/
Object doFilter(Object target, FilterChain filterChain);
}
/**
* 具體實現A攔截
**/
class FilterA implements Filter{
@Override
public Object doFilter(Object target, FilterChain filterChain){
System.out.println("A");
// 在這里選擇是否繼續往下走,這種方式會往下走
return filterChain.doFilter(target);
}
}
/**
* 具體實現B攔截
**/
class FilterB implements Filter{
@Override
public Object doFilter(Object target, FilterChain filterChain){
System.out.println("B");
// 這種方式會直接返回,不會繼續執行其他攔截器(當然了在我的例子中也沒有其他攔截器了)
return "";
}
}
/**
* 內部維護一個數組,存儲各個攔截器
**/
public static class FilterChain{
private List<Filter> filters = new ArrayList<>();
private Iterator iterator;
public Object doFilter(Object target){
if(iterator == null){
iterator = filters.iterator();
}
if(iterator.hasNext()){
Filter filter = (Filter) iterator.next();
filter.doFilter(target, this);
}
return target;
}
public void addFilter(Filter filter){
filters.add(filter);
}
}
/**
* 測試
**/
public static void main(String[] args) {
FilterChain filterChain = new FilterChain();
FilterA filterA = new FilterA();
FilterB filterB = new FilterB();
filterChain.addFilter(filterA);
filterChain.addFilter(filterB);
filterChain.doFilter("");
}
四、總結
以上兩種責任鏈的不同形式,其實是應對於不同的業務場景,當需要所有的攔截都走一輪,則采用第一種;當在某個攔截器失敗后不繼續進行,則采用第二種。在實際的場景中需要綜合考慮,采取最符合業務場景的形式進行編碼。