責任鏈模式實現的三種方式

責任鏈模式

責任鏈模式的定義:使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係, 將這個對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理他爲止。這裏就不再過多的介紹什麼是責任鏈模式,主要來說說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則適合在原有代碼基礎上,增加責任鏈模式代碼改動量最小的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章