1 Introduction
Chain of Responsibility pattern: the 多个对象
chance to handle the request, thus avoiding the request of 发送者
and 接受者
between 耦合
relationships. These objects together into a chain, and pass the request along the chain until an object handles it so far.
2. graphic
The new store opened, each order, you can enjoy multiple coupons superimposed relief
Chain of Responsibility pattern
3. Case realization
FIG following class
- The definition of a discount coupon
抽象类
; - Abstract class contains a reference to itself
nextDiscountFilter
, to the target strand strung together, the original method of calculating the price after discountcalculateBySourcePrice
;
Implementation class
FullDistcountFliter
Full 200 20 yuan;FirstPurchaseDiscount
First-time buyers minus 20 yuan;SecondPurchaseDiscountFilter
10% off the second member;HolidayDiscountFilter
All holiday minus 5 yuan.
Interface and implementation class
package com.wzj.chainOfResponsibility.example2;
/**
* @Author: wzj
* @Date: 2019/9/8 9:06
* @Desc: 折扣优惠接口
*/
public abstract class DiscountFilter {
// 下一个责任链成员
protected DiscountFilter nextDiscountFilter;
// 根据原价计算优惠后的价格
public abstract int calculateBySourcePrice(int price);
}
package com.wzj.chainOfResponsibility.example2;
/**
* @Author: wzj
* @Date: 2019/9/8 9:07
* @Desc: 满200减20元
*/
public class FullDiscountFilter extends DiscountFilter{
public int calculateBySourcePrice(int price) {
if (price > 200){
System.out.println("优惠满减20元");
price = price - 20;
}
if(this.nextDiscountFilter != null) {
return super.nextDiscountFilter.calculateBySourcePrice(price);
}
return price;
}
}
package com.wzj.chainOfResponsibility.example2;
/**
* @Author: wzj
* @Date: 2019/9/8 16:06
* @Desc: 首次购买减20元
*/
public class FirstPurchaseDiscount extends DiscountFilter {
public int calculateBySourcePrice(int price) {
if (price > 100){
System.out.println("首次购买减20元");
price = price - 20;
}
if(this.nextDiscountFilter != null) {
return super.nextDiscountFilter.calculateBySourcePrice(price);
}
return price;
}
}
package com.wzj.chainOfResponsibility.example2;
/**
* @Author: wzj
* @Date: 2019/9/8 16:09
* @Desc: 第二件打9折
*/
public class SecondPurchaseDiscountFilter extends DiscountFilter{
public int calculateBySourcePrice(int price) {
System.out.println("第二件打9折");
Double balance = price * 0.9;
if(this.nextDiscountFilter != null) {
return super.nextDiscountFilter.calculateBySourcePrice(balance.intValue());
}
return price;
}
}
package com.wzj.chainOfResponsibility.example2;
/**
* @Author: wzj
* @Date: 2019/9/8 16:02
* @Desc: 节日一律减5元
*/
public class HolidayDiscountFilter extends DiscountFilter{
public int calculateBySourcePrice(int price) {
if (price > 20){
System.out.println("节日一律减5元");
price = price - 5;
}
if(this.nextDiscountFilter != null) {
return super.nextDiscountFilter.calculateBySourcePrice(price);
}
return price;
}
}
Test category
package com.wzj.chainOfResponsibility.example2;
/**
* @Author: wzj
* @Date: 2019/9/8 16:17
* @Desc:
*/
public class TestDiscountFilter {
public static void main(String[] args) {
int price = 240;
String productStr = String.format("商品清单:苹果、香蕉、桔子, 商品总金额为:%d元.", price);
System.out.println(productStr);
//声明责任链上的所有节点
FullDiscountFilter fulDF = new FullDiscountFilter();
FirstPurchaseDiscount firstDF = new FirstPurchaseDiscount();
SecondPurchaseDiscountFilter secDF = new SecondPurchaseDiscountFilter();
HolidayDiscountFilter holDF = new HolidayDiscountFilter();
//设置链中的顺序:满减->首购减->第二件减->假日减
fulDF.nextDiscountFilter = firstDF;
firstDF.nextDiscountFilter = secDF;
secDF.nextDiscountFilter = holDF;
holDF.nextDiscountFilter = null;
int total = fulDF.calculateBySourcePrice(price);
System.out.println(String.format("所有商品优惠价后金额为:%d", total));
}
}
Results of the
商品清单:苹果、香蕉、桔子, 商品总金额为:240元.
优惠满减20元
首次购买减20元
第二件打9折
节日一律减5元
所有商品优惠价后金额为:175
4. Chain of Responsibility pattern handwriting application filter
Now there is such a scene, programmer Joe Smith to find objects in a dating show, in the first and second shut out the lights off are girls pass out, respectively, take a look at how the two girls are filtered Joe Smith.
Zhang Xiaomei dialogue
Zhang Xiaojing dialogue
This scenario can simulate how an http request through the filter to the server through each filter, it is conceivable to Joe Smith dating process through a checkpoint, because they do not meet the conditions of the filter are filtered out of the girl.
The first version v1
- And the definition of a class in response to a request class
MyRequest
,MyResponse
; - Define the filter
MyFilter
; - Implement class two filters
HeightFliter
,EducationalBackGroundFilter
respectively to achieve the height and degree filtration filter; - Defined chain
MyFilterChain
, implement an interfaceMyFilter
,add
the method to realize the filter in the chain add filter objects;doFilter
implemented method of filtering operations for all filter objects;
Codes are as follows
package com.wzj.chainOfResponsibility.example1.v1;
/**
* @Author: wzj
* @Date: 2019/9/7 20:14
* @Desc: 请求
*/
public class MyRequest {
String str;
}
package com.wzj.chainOfResponsibility.example1.v1;
/**
* @Author: wzj
* @Date: 2019/9/7 20:15
* @Desc: 响应
*/
public class MyResponse {
String str;
}
package com.wzj.chainOfResponsibility.example1.v1;
/**
* @Author: wzj
* @Date: 2019/9/7 20:00
* @Desc: 模拟实现过滤器
*/
public interface MyFilter {
void doFilter(MyRequest myRequest, MyResponse myResponse);
}
package com.wzj.chainOfResponsibility.example1.v1;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: wzj
* @Date: 2019/9/7 20:36
* @Desc: 责任链
*/
public class MyFilterChain implements MyFilter {
List<MyFilter> list = new ArrayList<MyFilter>();
public MyFilterChain add(MyFilter myFilter) {
list.add(myFilter);
return this;
}
public void doFilter(MyRequest myRequest, MyResponse myResponse) {
for(MyFilter f : list ){
f.doFilter(myRequest, myResponse);
}
}
}
package com.wzj.chainOfResponsibility.example1.v1;
/**
* @Author: wzj
* @Date: 2019/9/7 20:20
* @Desc: 身高过滤器
*/
public class HeightFliter implements MyFilter {
public void doFilter(MyRequest myRequest, MyResponse myResponse) {
myRequest.str = myRequest.str.replace("170", "个子有点矮");
myResponse.str += "【妹子挑剔,需要过滤身高】";
}
}
package com.wzj.chainOfResponsibility.example1.v1;
/**
* @Author: wzj
* @Date: 2019/9/7 20:33
* @Desc: 教育背景过滤器
*/
public class EducationalBackGroundFilter implements MyFilter {
public void doFilter(MyRequest myRequest, MyResponse myResponse) {
myRequest.str = myRequest.str.replace("学历大专", "学历不高");
myResponse.str += "【妹子挑剔,需要过滤学历】";
}
}
Test category
package com.wzj.chainOfResponsibility.example1.v1;
/**
* @Author: wzj
* @Date: 2019/9/7 20:42
* @Desc: 直观的方式处理response,
* 并将response的处理放在request的下面
*/
public class TestV1 {
public static void main(String[] args) {
MyRequest myRequest = new MyRequest();
myRequest.str = "张三身高170,学历大专,跪求妹子给个机会认识";
System.out.println("request:" + myRequest.str);
MyResponse myResponse = new MyResponse();
myResponse.str = "";
MyFilterChain chain = new MyFilterChain();
chain.add(new HeightFliter()).add(new EducationalBackGroundFilter());
chain.doFilter(myRequest, myResponse);
System.out.println("response:" + myResponse.str);
}
}
result
request:张三身高170,学历大专,跪求妹子给个机会认识
response:【妹子挑剔,需要过滤身高】【妹子挑剔,需要过滤学历】
There are the following requirements, in order to better simulate a complete request, at 过滤请求时顺序,响应请求时逆序
, what way to do it?
The second version v2
MyRequest
Class andMyResponse
with v1- In
MyFilterChain
addition of the specific responsibilities of the recording object subscript chainindex
, to achieve the position of the recording function; doFilter
The method which implement the recursive call, and the current chainMyFilterChain
has been passed back as a parameter;
embodied as
package com.wzj.chainOfResponsibility.example1.v2;
/**
* @Author: wzj
* @Date: 2019/9/7 20:00
* @Desc: 模拟实现过滤器
*/
public interface MyFilter {
void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain);
}
package com.wzj.chainOfResponsibility.example1.v2;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: wzj
* @Date: 2019/9/7 20:36
* @Desc: 在MyFilterChain中处理加入位置的记录index
*/
public class MyFilterChain implements MyFilter {
List<MyFilter> list = new ArrayList<MyFilter>();
int index = 0;
public MyFilterChain add(MyFilter myFilter) {
list.add(myFilter);
return this;
}
public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
if(index == list.size())
return;
MyFilter myFilter = list.get(index);
index ++;
myFilter.doFilter(myRequest, myResponse, myFilterChain);
}
}
package com.wzj.chainOfResponsibility.example1.v2;
/**
* @Author: wzj
* @Date: 2019/9/7 20:20
* @Desc: 身高过滤器
*/
public class HeightFliter implements MyFilter {
public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
myRequest.str = myRequest.str.replace("170", "个子有点矮");
myFilterChain.doFilter(myRequest, myResponse, myFilterChain);
myResponse.str += "【妹子挑剔,需要过滤身高】";
}
}
package com.wzj.chainOfResponsibility.example1.v2;
/**
* @Author: wzj
* @Date: 2019/9/7 20:33
* @Desc: 教育背景过滤器
*/
public class EducationalBackGroundFilter implements MyFilter {
public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
myRequest.str = myRequest.str.replace("学历大专", "学历不高");
myFilterChain.doFilter(myRequest, myResponse, myFilterChain);
myResponse.str += "【妹子挑剔,需要过滤学历】";
}
}
Test category
package com.wzj.chainOfResponsibility.example1.v2;
/**
* @Author: wzj
* @Date: 2019/9/7 20:42
* @Desc: 过滤请求时顺序,响应请求时逆序,在MyFilterChain中处理加入位置的记录,
* 同时在MyFilter中加入第三个参数MyFilterChain,让链条递归实现倒序
*/
public class TestV2 {
public static void main(String[] args) {
MyRequest myRequest = new MyRequest();
myRequest.str = "张三身高170,学历大专,跪求妹子给个机会认识";
System.out.println("request:" + myRequest.str);
MyResponse myResponse = new MyResponse();
myResponse.str = "";
MyFilterChain chain = new MyFilterChain();
chain.add(new HeightFliter()).add(new EducationalBackGroundFilter());
chain.doFilter(myRequest, myResponse, chain);
System.out.println("response:" + myResponse.str);
}
}
The results are displayed first filtration degree, filtered height.
request:张三身高170,学历大专,跪求妹子给个机会认识
response:【妹子挑剔,需要过滤学历】【妹子挑剔,需要过滤身高】
Since MyFilterChain
doing doFilter
, the current is always passed is MyFilterChain
the same instance, it can be simplified MyFilterChain
.
The third version v3
MyFilterChain
Removal ofMyFilter
the interface;doFilter
The method of removing the third parameterMyFilterChain
;
specific implementation is as follows
package com.wzj.chainOfResponsibility.example1.v3;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: wzj
* @Date: 2019/9/7 20:36
* @Desc: 过滤器完全模式,去掉实现接口,并将doFilter方法中的chain
*/
public class MyFilterChain{
List<MyFilter> list = new ArrayList<MyFilter>();
int index = 0;
public MyFilterChain add(MyFilter myFilter) {
list.add(myFilter);
return this;
}
public void doFilter(MyRequest myRequest, MyResponse myResponse) {
if(index == list.size())
return;
MyFilter myFilter = list.get(index);
index ++;
myFilter.doFilter(myRequest, myResponse, this);
}
}
package com.wzj.chainOfResponsibility.example1.v3;
/**
* @Author: wzj
* @Date: 2019/9/7 20:20
* @Desc: 身高过滤器
*/
public class HeightFliter implements MyFilter {
public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
myRequest.str = myRequest.str.replace("170", "个子有点矮");
myFilterChain.doFilter(myRequest, myResponse);
myResponse.str += "【妹子挑剔,需要过滤身高】";
}
}
package com.wzj.chainOfResponsibility.example1.v3;
/**
* @Author: wzj
* @Date: 2019/9/7 20:33
* @Desc: 教育背景过滤器
*/
public class EducationalBackGroundFilter implements MyFilter {
public void doFilter(MyRequest myRequest, MyResponse myResponse, MyFilterChain myFilterChain) {
myRequest.str = myRequest.str.replace("学历大专", "学历不高");
myFilterChain.doFilter(myRequest, myResponse);
myResponse.str += "【妹子挑剔,需要过滤学历】";
}
}
Test category
package com.wzj.chainOfResponsibility.example1.v3;
/**
* @Author: wzj
* @Date: 2019/9/7 20:42
* @Desc: 过滤器完全模式
*/
public class TestV3 {
public static void main(String[] args) {
MyRequest myRequest = new MyRequest();
myRequest.str = "张三身高170,学历大专,跪求妹子给个机会认识";
System.out.println("request:" + myRequest.str);
MyResponse myResponse = new MyResponse();
myResponse.str = "";
MyFilterChain chain = new MyFilterChain();
chain.add(new HeightFliter()).add(new EducationalBackGroundFilter());
chain.doFilter(myRequest, myResponse);
System.out.println("response:" + myResponse.str);
}
}
result
request:张三身高170,学历大专,跪求妹子给个机会认识
response:【妹子挑剔,需要过滤学历】【妹子挑剔,需要过滤身高】
5. Source Filter Analysis
In web projects often need to be configured to meet a variety of filters filter we need to meet our custom filter information, do not bother to look at how the tomcat source code is done, with the following tomcat8.5.37
source code to analyze the implementation.
Find a ApplicationFilterFactory
class in which the createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet)
method creates the chain of responsibility,
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
// 如何没有servlet执行,返回null
if (servlet == null)
return null;
// 创建和初始化过滤器链对象
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// 获取上下中的过滤器的映射集合
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0))
return (filterChain);
// 获取匹配的过滤器映射信息
DispatcherType dispatcher =
(DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null){
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
// 对过滤器链添加对应的路径匹配过滤器
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// 添加匹配servlet name的过滤器
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMaps[i], servletName))
continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// 返回过滤器责任链
return filterChain;
}
Performing filter doFilter
method, calls a internalDoFilter () method
public final class ApplicationFilterChain implements FilterChain {
......
/**
* 维护当前过滤器链的位置信息
*/
private int pos = 0;
/**
* 当前过滤器的长度
*/
private int n = 0;
......
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
internalDoFilter(request,response);
}
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// 如果有下一个,则调用下一个过滤器
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
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的doFilter()方法 , doFilter 又会调用 internalDoFilter,一直递归下去, 直到调用完所有的过滤器
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;
}
// 从最后一个过滤器开始调用
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 {
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);
}
}
}
}
6. Chain of Responsibility pattern summary
advantage
- Separate request by the sender and the receiving process. Requester may not know who is handling the deal can not know the whole picture requests, both decoupling and improve the flexibility of the system.
Shortcoming
- Performance issues, traversing each request from the head of the chain to the end of a chain, particularly when relatively long chain, performance is a very big problem;
- Debugging is not very convenient, especially long chain, similar recursive manner, when debugging the logic can be complex.
Precautions
- Need to control the number of nodes in the chain, to avoid occurrence of long-chain, the general practice is to set a maximum number of nodes in the Handler, it is determined whether the threshold is exceeded, beyond which it does not allow the chain to establish setNext process, avoid unintentional destroy system performance.