責任鍊模式
責任鍊模式的定義:使多個對象都有機會處理請求,進而避免請求的發送者和接受者之間的耦合關系, 将這個對象連成一條鍊,并沿着這條鍊傳遞該請求,直到有一個對象處理他為止。這裡就不再過多的介紹什麼是責任鍊模式,主要來說說java中如何編寫。主要從下面3個架構中的代碼中介紹。
- servlet中的filter
- dubbo中的filter
- mybatis中的plugin 這3個架構在實作責任鍊方式不盡相同。
servlet中的Filter
servlet中分别定義了一個 Filter和FilterChain的接口,核心代碼如下:
public final class ApplicationFilterChain implements FilterChain {
private int pos = 0; //目前執行filter的offset
private int n; //目前filter的數量
private ApplicationFilterConfig[] filters; //filter配置類,通過getFilter()方法擷取Filter
private Servlet servlet
@Override
public void doFilter(ServletRequest request, ServletResponse response) {
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
} else {
// filter都處理完畢後,執行servlet
servlet.service(request, response);
}
}
}
代碼還算簡單,結構也比較清晰,定義一個Chain,裡面包含了Filter清單和servlet,達到在調用真正servlet之前進行各種filter邏輯。

Dubbo中的Filter
Dubbo在建立Filter的時候是另外一個方法,通過把Filter封裝成 Invoker的匿名類,通過連結清單這樣的資料結構來完成責任鍊,核心代碼如下:
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
//隻擷取滿足條件的Filter
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (filters.size() > 0) {
for (int i = filters.size() - 1; i >= 0; i --) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
...
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
...
};
}
}
return last;
}
Dubbo的責任鍊就沒有類似FilterChain這樣的類吧Filter和調用Invoker結合起來,而是通過建立一個連結清單,調用的時候我們隻知道第一個節點,每個節點包含了下一個調用的節點資訊。這裡的雖然Invoker封裝Filter沒有顯示的指定next,但是通過java匿名類和final的機制達到同樣的效果。
Mybatis中的Plugin
Mybatis可以配置各種Plugin,無論是官方提供的還是自己定義的,Plugin和Filter類似,就在執行Sql語句的時候做一些操作。Mybatis的責任鍊則是通過動态代理的方式,使用Plugin代理實際的Executor類。(這裡實際還使用了組合模式,因為Plugin可以嵌套代理),核心代碼如下:
public class Plugin implements InvocationHandler{
private Object target;
private Interceptor interceptor;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (滿足代理條件) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
}
//對傳入的對象進行代理,可能是實際的Executor類,也可能是Plugin代理類
public static Object wrap(Object target, Interceptor interceptor) {
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
}
簡單的示意圖如下:
總結
這裡簡單介紹了Servlet、Dubbo、Mybatis對責任鍊模式的不同實作手段,其中Servlet是相對比較清晰,又易于實作的方式,而Dubbo和Mybatis則适合在原有代碼基礎上,增加責任鍊模式代碼改動量最小的。