一、模式解析
責任鏈模式是一種對象的行為模式。在責任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織和分配責任。
責任鏈模式的要點主要是:
1、有多個對象共同對一個任務進行處理。
2、這些對象使用鏈式存儲結構,形成一個鏈,每個對象知道自己的下一個對象。
3、一個對象對任務進行處理,可以添加一些操作后將對象傳遞個下一個任務。也可以在此對象上結束任務的處理,並結束任務。
3、客戶端負責組裝鏈式結構,但是客戶端不需要關心最終是誰來處理了任務。
二、模式代碼
1、創建處理器接口
package chain.patten; /** * 責任鏈接口 * @author zjl * @time 2016-2-11 * */ public abstract class Handler { //下一級責任鏈 public Handler handler; //設置下一級責任鏈 public void setSuccessor(Handler handler){ this.handler=handler; } public abstract void request(int request); }
2、創建處理器對象1
package chain.patten; public class ConcreteHandler1 extends Handler { @Override public void request(int request) { if(request<10){ System.out.println("我是handler1,我處理了請求:"+request); }else { this.handler.request(request); } } }
3、創建處理器對象2
package chain.patten; public class ConcreteHandler2 extends Handler { @Override public void request(int request) { if(request>10){ System.out.println("我是handler2,我處理了請求:"+request); }else { System.out.println("請求"+request+"沒人能處理"); } } }
4、創建客戶端
package chain.patten; public class Client { public static void main(String[] args) { //創建處理器 Handler handler1=new ConcreteHandler1(); Handler handler2=new ConcreteHandler2(); //客戶端創建處理器的關聯,形成鏈 handler1.setSuccessor(handler2); //創建任務,此處為一些數字,不同大小,處理器處理結果不同 int[] requests={4,10,59,2,16}; //調用處理器處理 for(int request:requests){ handler1.request(request); } } }
5、執行結果
我是handler1,我處理了請求:4 請求10沒人能處理 我是handler2,我處理了請求:59 我是handler1,我處理了請求:2 我是handler2,我處理了請求:16
三、應用場景
在工作中,尤其是java web開發中,有兩個地方明顯使用責任鏈模式,一個是filter,一個是listener,filter的自定義在web開發中可以對web請求做各種處理和過濾,包括:對請求和相應的字符集處理、對跨站腳本攻擊的過濾、獲取客戶端真實ip地址、獲取客戶證書、防止盜鏈等等,在此處,簡單模擬責任鏈模式對請求的處理。
四、場景代碼
1、過濾器接口
package chain.example; public abstract class Filter { //request 和response在真正的servlet中是對象,此處簡化處理為string public abstract void doFilter(String request,String response,FilterChain filterChain); }
2、過濾器-處理字符集
package chain.example; public class EncodeFilter extends Filter { @Override public void doFilter(String request, String response, FilterChain filterChain) { System.out.println("對request做utf-8編碼"); filterChain.doFilter(request, response); System.out.println("對response做utf-8編碼"); } }
3、過濾器-處理xss攻擊
package chain.example; public class XssFilter extends Filter { @Override public void doFilter(String request, String response, FilterChain filterChain) { System.out.println("過濾request的xss內容"); filterChain.doFilter(request, response); System.out.println("過濾response的xss內容"); } }
4、servlet接口,僅實現service接口
package chain.example; public interface Servlet { public void service(String request,String response); }
5、定義一個servlet實現
package chain.example; public class MainServlet implements Servlet { @Override public void service(String request, String response) { System.out.println(request); //為response賦值 response="返回結果"; System.out.println(response); } }
6、定義內部處理的filter鏈,鏈中保存真正filter的執行順序,和servlet
package chain.example; import java.util.ArrayList; import java.util.List; public class FilterChain { private int cursor; public List<Filter> filters=new ArrayList<Filter>(); public Servlet servlet; public void setServlet(Servlet servlet){ this.servlet=servlet; } public void addFilter(Filter filter){ this.filters.add(filter); } public void doFilter(String request,String response){ if(cursor<filters.size()){ filters.get(cursor++).doFilter(request, response,this); }else { servlet.service(request, response); } } }
7、客戶端代碼
package chain.example; public class Client { public static void main(String[] args) { //定義filter Filter encodeFilter=new EncodeFilter(); Filter xssFilter=new XssFilter(); FilterChain chain=new FilterChain(); chain.addFilter(encodeFilter); chain.addFilter(xssFilter); //定義servlet Servlet servlet=new MainServlet(); chain.setServlet(servlet); chain.doFilter("發送請求", ""); } }
8.、執行結果
對request做utf-8編碼
過濾request的xss內容
發送請求
返回結果
過濾response的xss內容
對response做utf-8編碼
五、實例說明
filter的責任鏈實現與責任鏈模式的標准代碼有着一定的差距,它具有如下特點:
1、責任鏈的實現並不是鏈式結構,而是以一個FilterChain保存了所有責任鏈的引用,通過FilterChain的doFilter方法依次調用filter進行執行;
2、filter中同時也保存了FilterChain的引用,形成了一個雙向引用;
3、FilterChain作為web容器的功能,由系統默認提供,我們無需關注其實現原理(注:代碼中僅為可能實現的方式)。他依次將web.xml中的filter按照文件排放順序進行調用執行。
由此我們思考,責任鏈模式的實現應該有多種形式,可以為責任鏈之間的互相鏈式引用,也可以為第三方集合中的順序執行方式,在我所接觸最廣泛的系統中,對責任鏈進行如下使用:在執行真正交易時候,根據模板選擇責任鏈,在配置文件中定義了一些列的Command,由chain方法將其轉化為一個list的責任鏈,然后使用迭代器依次執行,對系統的傳遞請求進行一些列操作,格式為:
<chain id="chainForRoleControlDV">
<commands>
<ref>RuleBasedDynamicViewCommand</ref>
<ref>roleControlCommand</ref>
<ref>validationCommand</ref>
<ref>ruleCommand</ref>
<ref>delegateCommand</ref>
<ref>${chain.monitor}</ref>
</commands>
</chain>
此種可以對請求進行登錄控制,字段校驗,權限控制,監控信息發送,驗證碼校驗等一些列操作。這樣只需要定義幾個責任鏈可以完成對所有交易的區分控制。
