责任链模式在设计模式里边属于比较难的设计模式,主要用于对一个对象进行一系列的操作,每个操作相互独立。这一系列的操作组成了一个操作链,每个操作都有其具体的职责,即是一个责任链。
UML图如下
先用一个简单的例子来体验一下责任链模式,假设有个程序需要接收一个消息,但是这个消息需要进行过滤,比如HTML,JS的转义,敏感字符的替换等。
定义一个消息类,只有消息这一个属性;定义一个过滤器,只有过滤这一个方法,然后给出三个实现,分别用于HTML,JS和敏感字符的过滤。
public class Msg {
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
}
public interface Filter {
/**
* 过滤方法
* @param msg
* @return true:继续执行; false: 不继续执行
*/
boolean doFilter(Msg msg);
}
public class HTMLFilter implements Filter {
@Override
public boolean doFilter(Msg msg) {
String text = msg.getText();
text = text.replaceAll("<", "[");
text = text.replaceAll(">", "]");
msg.setText(text);
return true;
}
}
public class JSFilter implements Filter {
@Override
public boolean doFilter(Msg msg) {
String text = msg.getText();
msg.setText(text.replaceAll("javascript", "cdth"));
return true;
}
}
public class SensitiveFilter implements Filter{
private final String sensitiveText = "996" ;
@Override
public boolean doFilter(Msg msg) {
String text = msg.getText();
msg.setText(text.replaceAll(sensitiveText, "955"));
return false;
}
}
责任链模式最主要的地方就是实现责任链,我们可以定义一个类,维护一个过滤器的集合,在调用过滤方法的时候把集合里的过滤器遍历,依次调用其自身的过滤方法,且根据返回值来判断过滤器链要不要继续往下执行。这里可以看到,过滤器链本身也实现了Filter接口,这说明我们可以把该过滤器链接到其他的过滤器链的后边。
public class FilterChain implements Filter {
private List<Filter> filters = new ArrayList<>();
public FilterChain addFilter(Filter filter) {
filters.add(filter);
return this;
}
@Override
public boolean doFilter(Msg msg) {
for (Filter filter : filters) {
if(!filter.doFilter(msg)) {
return false;
}
}
return true;
}
}
测试一下。
public class Main {
public static void main(String[] args) {
Msg msg = new Msg();
msg.setText("<p>i love javascript</p><p>i hate 996</p>");
FilterChain chain = new FilterChain();
chain.addFilter(new HTMLFilter())
.addFilter(new SensitiveFilter())
.addFilter(new JSFilter());
chain.doFilter(msg);
System.out.println(msg);
}
}
在JAVAEE 中,过滤器Filter接口其实就是一种责任链模式的实现,简单写个DEMO来模仿一下Filter的原理。
首先我们需要ServletRequest,ServletResponse,和FilterChain 三个对象,其中ServletRequest,ServletResponse只有一个属性Body,FilterChain对象暂时没有任何东西。
public class ServletRequest {
private String body;
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
@Override
public String toString() {
return body;
}
}
public class ServletResponse {
private String body;
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
@Override
public String toString() {
return body;
}
}
public class FilterChain {
}
然后,我们需要一个Filter接口,用于对ServletRequest和ServletResponse进行过滤,且可以顺着过滤器链一直往下执行。所以Filter接口需要三个参数ServletRequest,ServletResponse和FilterChain。其中让过滤器开始工作的入口是过滤器链提供的,所以FilteChain需要有一个方法(public void doFilter(ServletRequest request, ServletResponse response) )来让过滤器链中的过滤器开始工作。再添加三个Filter接口的实现,同样是对HTML,JS,敏感词的过滤。
public interface Filter {
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain);
}
public class FilterChain {
public void doFilter(ServletRequest request, ServletResponse response) {}
}
public class HTMLFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// start process request
String body = request.getBody();
body = body.replaceAll("<", "[");
body = body.replaceAll(">", "]");
request.setBody(body + " --html filter");
// next filter of chain start process request
chain.doFilter(request, response);
// request process finished, start process response
response.setBody(response.getBody() + " --html filter");
}
}
public class JSFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// start process request
request.setBody(request.getBody().replaceAll("javascript", "cdth") + " --js filter");
// next filter of chain start process request
chain.doFilter(request, response);
// request process finished, start process response
response.setBody(response.getBody() + " --js filter");
}
}
public class SensitiveFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// start process request
request.setBody(request.getBody().replaceAll("996", "955") + " --sensitive filter");
// next filter of chain start process request
chain.doFilter(request, response);
// request process finished, start process response
response.setBody(response.getBody() + " --sensitive filter");
}
}
可以看到每个过滤器的实现都是先处理request,再交给链条中的下一个过滤器去处理request,然后再处理response。因为JAVAEE标准就是对request的处理和对response的处理,其顺序是正好相反的;例如,处理request的顺序是HTMLFilter,JSFilter,SensitiveFilter,那么处理repsonse的顺序就是SensitiveFilter,JSFilter,HTMLFilter。其实Spring MVC的拦截器调用preHandle和postHandle的顺序,也是这样的。
现在需要做的就是实现FilterChain,首先肯定需要把过滤器都存起来,所以需要集合和addFilter方法。重点就在于doFilter方法,为了保证上述的顺序,需要有一个index来保证当前使用的过滤器。
以下是FilterChain的完整实现。
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
private int index;
public FilterChain addFilter(Filter filter) {
filters.add(filter);
return this;
}
public void doFilter(ServletRequest request, ServletResponse response) {
if(index < filters.size()) {
Filter filter = filters.get(index);
index++;
filter.doFilter(request, response, this);
}
}
}
测试一下。
public class Main {
public static void main(String[] args) {
ServletRequest request = new ServletRequest();
request.setBody("<p>i love javascript</p><p>i hate 996</p>");
ServletResponse response = new ServletResponse();
response.setBody("response");
FilterChain chain = new FilterChain();
chain.addFilter(new HTMLFilter())
.addFilter(new SensitiveFilter())
.addFilter(new JSFilter());
chain.doFilter(request,response);
System.out.println("---------request-----------");
System.out.println(request.getBody());
System.out.println("---------response-----------");
System.out.println(response.getBody());
}
}
运行之后,程序确实如期的执行。
现在再回到Filter上来,虽然我们把三个过滤器都添加进了过滤器链,但是如果我其中的一个过滤器没有去让链条往下执行,那么链条实际上会在这个过滤器的地方“递归回去”。
现在 SensitiveFilter 处于链条中的第二个位置,如果我把chain.doFilter(request, response)注释掉,那么过滤器链就会在他这里终止不会去找下一个过滤器即JSFilter。
public class SensitiveFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// start process request
request.setBody(request.getBody().replaceAll("996", "955") + " --sensitive filter");
// next filter of chain start process request
//chain.doFilter(request, response);
// request process finished, start process response
response.setBody(response.getBody() + " --sensitive filter");
}
}