什么是责任链模式?
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
最经典的是servlet的Filter、Spring中各种拦截器
责任链模式的真实场景
一个请求经过A处理,在经过B处理,在经过C处理,会两种处理方式:
- 请求会经过A、B、C所有都处理一遍,不会终止。
- 请求经过链条中,经过某一处理时会存在中断的情况,并且不会往下执行。比如经过B处理时中断,那么C处理就不会执行。
- Servlet中的责任链的模式,实现的效果先经过每个Request,然后经过每个Response过滤。如下图:
第一种不会终止的例子
import java.util.ArrayList;
import java.util.List;
interface MyStringFilter {
/**
* @param message 需要处理的消息
* @return 返回处理后的消息
*/
String process(String message);
}
/**
* 敏感词过滤替换
*/
class SensitiveStringFilter implements MyStringFilter {
@Override
public String process(String message) {
System.out.println("执行敏感词替换......");
return message.replaceAll("1024", "1000");
}
}
/**
* 脚本过滤替换,如<scripr>alter("11")</scripr>
*/
class HtmlStringFilter implements MyStringFilter {
@Override
public String process(String message) {
System.out.println("脚本过滤替换......");
return message.replaceAll("<", "[").replaceAll(">", "]");
}
}
/**
* 什么都不影响,实际在摸鱼
*/
class NothingFilter implements MyStringFilter {
@Override
public String process(String message) {
//doing 模拟不经过处理,但不影响
System.out.println("什么都不影响,实际在摸鱼......");
return message;
}
}
/**
* 定义个责任链的链条
*/
class StringChainFilter {
List<MyStringFilter> myStringFilterList = new ArrayList<>();
public StringChainFilter add(MyStringFilter filter) {
myStringFilterList.add(filter);
return this;
}
public String process(String message) {
String processAfter = null;
for (int i = 0; i < myStringFilterList.size(); i++) {
processAfter = myStringFilterList.get(i).process(message);
}
return processAfter;
}
}
//测试
class DemoTest {
public static void main(String[] args) {
String message = "你好,这是测试责任链模式字符串,<script>alter('1');</script>,1024 快乐";
StringChainFilter chainFilter = new StringChainFilter();
chainFilter.add(new SensitiveStringFilter())
.add(new HtmlStringFilter())
.add(new NothingFilter());
String afterMessage = chainFilter.process(message);
System.out.println("处理前字符串=" + message);
System.out.println("处理后字符串=" + afterMessage);
}
}
运行的结果:
执行敏感词替换......
脚本过滤替换......
什么都不影响,实际在摸鱼......
处理前字符串=你好,这是测试责任链模式字符串,<script>alter('1');</script>,1024 快乐
处理后字符串=你好,这是测试责任链模式字符串,<script>alter('1');</script>,1000 快乐
第二种会终止的例子
import java.util.ArrayList;
import java.util.List;
/**
* Created by lizhiming on 2022/1/20.
*/
public interface MyStringStopFilter {
/**
* @param message 需要处理的消息
* @return 是否需要终止,返回true责任链中断
*/
boolean process(Message message);
}
class Message {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "Message{" +
"message='" + message + ''' +
'}';
}
}
/**
* 敏感词过滤替换
*/
class SensitiveStopStringFilter implements MyStringStopFilter {
@Override
public boolean process(Message message) {
System.out.println("执行敏感词替换......");
String msg = message.getMessage().replaceAll("1024", "1000");
message.setMessage(msg);
return true;
}
}
/**
* 脚本过滤替换,如<scripr>alter("11")</scripr>
*/
class HtmlStopStringFilter implements MyStringStopFilter {
@Override
public boolean process(Message message) {
System.out.println("脚本过滤替换......");
String msg = message.getMessage().replaceAll("<", "[").replaceAll(">", "]");
message.setMessage(msg);
return false;
}
}
/**
* 什么都不影响,实际在摸鱼
*/
class NothingStopStringFilter implements MyStringStopFilter {
@Override
public boolean process(Message message) {
//doing 模拟不经过处理,但不影响
System.out.println("什么都不影响,实际在摸鱼......");
return false;
}
}
/**
* 处理责任链
*/
class MyStringStopChailFilter {
List<MyStringStopFilter> myStringStopFilterList = new ArrayList<>();
public MyStringStopChailFilter add(MyStringStopFilter myStringStopFilter) {
myStringStopFilterList.add(myStringStopFilter);
return this;
}
public void process(Message message) {
for (int i = 0; i < myStringStopFilterList.size(); i++) {
//如果处理返回true,则不往下进行
if (myStringStopFilterList.get(i).process(message)) {
break;
}
}
}
}
/**
* 测试
*/
class MyStringStopFilterDemo {
public static void main(String[] args) {
String messageStr = "你好,这是测试责任链模式字符串,<script>alter('1');</script>,1024 快乐";
Message message = new Message();
message.setMessage(messageStr);
System.out.println("处理前字符串=" + message.getMessage());
//定义责任链处理顺序, SensitiveStopStringFilter 会中断,
//NothingStopStringFilter不会运行
MyStringStopChailFilter myStringStopChailFilter = new MyStringStopChailFilter();
myStringStopChailFilter.add(new HtmlStopStringFilter())
.add(new SensitiveStopStringFilter())
.add(new NothingStopStringFilter());
myStringStopChailFilter.process(message);
System.out.println("处理后字符串=" + message.getMessage());
}
}
运行结果:
处理前字符串=你好,这是测试责任链模式字符串,<script>alter('1');</script>,1024 快乐
脚本过滤替换......
执行敏感词替换......
处理后字符串=你好,这是测试责任链模式字符串,[script]alter('1');[/script],1000 快乐
第三种先经过Request,在经过Response
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
class Request {
private String requestStr;
}
@Data
class Response {
private String responseStr;
}
interface FilterRequestResponse {
void doFilter(Request request, Response response, FilterChain filterChain);
}
class HtmlFilter implements FilterRequestResponse {
@Override
public void doFilter(Request request, Response response, FilterChain filterChain) {
//先执行filter前面的request
String str = request.getRequestStr();
str = str.replaceAll(">", "]").replaceAll("<", "[");
request.setRequestStr(str);
filterChain.doFilter(request, response);
response.setResponseStr(response.getResponseStr() + "---经过HtmlFilter处理");
}
}
class SensitiveFilter implements FilterRequestResponse {
@Override
public void doFilter(Request request, Response response, FilterChain filterChain) {
//先执行filter前面的request
String str = request.getRequestStr();
str = str.replaceAll("1024", "1000");
request.setRequestStr(str);
filterChain.doFilter(request, response);
response.setResponseStr(response.getResponseStr() + "---经过SensitiveFilter处理");
}
}
class FilterChain {
List<FilterRequestResponse> filterRequestResponses = new ArrayList<>();
//当前的责任链中的初始位置
int orderIndex = 0;
public FilterChain add(FilterRequestResponse filterRequestResponse) {
filterRequestResponses.add(filterRequestResponse);
return this;
}
public void doFilter(Request request, Response response) {
//如果当前位置和责任链长度一样,说明每个链条都执行过,就返回
if (orderIndex == filterRequestResponses.size()) {
return;
}
//拿出第一个过滤器
FilterRequestResponse requestResponse = filterRequestResponses.get(orderIndex);
orderIndex++; //++之后,下次运行到上一句就是下一个
//执行当前责任链的
requestResponse.doFilter(request, response, this);
}
}
class Test {
public static void main(String[] args) {
Request request = new Request();
request.setRequestStr("大家好<script>,欢迎,欢迎来到1024 ");
Response response = new Response();
response.setResponseStr("我是需要返回值处理的");
FilterChain filterChain = new FilterChain();
filterChain.add(new HtmlFilter()).add(new SensitiveFilter());
filterChain.doFilter(request, response);
System.out.println(request.getRequestStr());
System.out.println(response.getResponseStr());
}
}
运行结果:
大家好[script],欢迎,欢迎来到1000
我是需要返回值处理的---经过SensitiveFilter处理---经过HtmlFilter处理
servlet中的filter
参考源码:org.apache.catalina.core.ApplicationFilterChain#internalDoFilter
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
// pos 当前执行filter的偏移量
// n 当前filter的数量
if (pos < n) {
//偏移量+1
ApplicationFilterConfig filterConfig = filters[pos++];
try {
//拿出一个filter
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
//执行具体的filter
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
//filter全部执行完成,继续往下走
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}