java 責任鏈模式的三種實現


責任鏈模式

責任鏈模式的定義:使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系, 將這個對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理他為止。這里就不再過多的介紹什么是責任鏈模式,主要來說說java中如何編寫。主要從下面3個框架中的代碼中介紹。

  • servlet中的filter
  • dubbo中的filter
  • mybatis中的plugin 這3個框架在實現責任鏈方式不盡相同。

servlet中的Filter

servlet中分別定義了一個 Filter和FilterChain的接口,核心代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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的匿名類,通過鏈表這樣的數據結構來完成責任鏈,核心代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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可以嵌套代理),核心代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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則適合在原有代碼基礎上,增加責任鏈模式代碼改動量最小的。

上面是轉發別人的。

下面是個人的模擬實現

1.web filter中的過濾器很多人都了解,所以不做模擬

2.dubbo中filter

public void test() {
        List<Filter> lists = new ArrayList<>();
        lists.add(new SayWorldFilter());
        lists.add(new SayHelloFilter());

        Request request = new MyRequest();

        for (Filter filter : lists) {
            Request next = request;
            request = new Request() {
                @Override
                public String test() {
                    return filter.filter(next);
                }
            };
        }
        request.test();
    }

3.mybatis 中使用動態代理生產的包裝類實現過濾

Request request = (Request) (RequestWrap.wrap(new MyRequest(), new SayWorldFilter()));
        request = (Request) (RequestWrap.wrap(request, new SayHelloFilter()));
        request.test();

 

具體代碼見附件。

https://files.cnblogs.com/files/z-test/filter.rar

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM