责任链模式(Chain of Responsibility):使多个对象
都有机会处理请求,从而避免了请求的发送者
和接受者
之间的耦合
关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。java
商城新开张,每一个订单,能够享受多张优惠券叠加减免
程序员
责任链模式
web
类图以下
tomcat
抽象类
;nextDiscountFilter
,用来把对象串成链,原价计算优惠后的价格方法calculateBySourcePrice
;实现类
app
FullDistcountFliter
满200减20元;FirstPurchaseDiscount
首次购买减20元;SecondPurchaseDiscountFilter
第二件打9折;HolidayDiscountFilter
节日一概减5元.接口与实现类ide
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; } }
测试类源码分析
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)); } }
执行结果性能
商品清单:苹果、香蕉、桔子, 商品总金额为:240元. 优惠满减20元 首次购买减20元 第二件打9折 节日一概减5元 全部商品优惠价后金额为:175
如今有这样一个场景,程序员张三在某相亲节目中找对象,在第一关和第二关分别被女孩灭灯pass掉,下面分别看看俩姑娘是如何过滤张三的。
小美对话张三
测试
此场景能够模拟一个http请求如何通过过滤器到服务端,通过每个过滤器,能够想象为张三相亲过程当中通过某个关卡,因为不符合该过滤器的条件被姑娘过滤掉。
this
第一个版本v1
MyRequest
、MyResponse
;MyFilter
;HeightFliter
、EducationalBackGroundFilter
,分别实现身高过滤和学历过滤;MyFilterChain
,实现接口MyFilter
,add
方法实现往过滤链条里添加过滤对象;doFilter
方法实现全部过滤对象的过滤操做;实现代码以下
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 += "【妹子挑剔,须要过滤学历】"; } }
测试类
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); } }
结果
request:张三身高170,学历大专,跪求妹子给个机会认识 response:【妹子挑剔,须要过滤身高】【妹子挑剔,须要过滤学历】
如今有以下需求,为更好的模拟一次完整请求,在过滤请求时顺序,响应请求时逆序
,有何办法能够作到呢?
第二个版本v2
MyRequest
类和MyResponse
同v1MyFilterChain
中加入记录具体责任链对象的下标index
,实现记录位置功能;doFilter
方法里面实现递归调用,并把当前的链条MyFilterChain
做为参数一直向后传递;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 += "【妹子挑剔,须要过滤学历】"; } }
测试类
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); } }
结果中显示先过滤学历,后过滤身高。
request:张三身高170,学历大专,跪求妹子给个机会认识 response:【妹子挑剔,须要过滤学历】【妹子挑剔,须要过滤身高】
因为MyFilterChain
在作doFilter
时,始终传递的是当前MyFilterChain
的同一个实例,故能够简化MyFilterChain
。
第三个版本v3
MyFilterChain
去除MyFilter
接口;doFilter
方法去除第三个参数MyFilterChain
;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 += "【妹子挑剔,须要过滤学历】"; } }
测试类
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); } }
结果
request:张三身高170,学历大专,跪求妹子给个机会认识 response:【妹子挑剔,须要过滤学历】【妹子挑剔,须要过滤身高】
在web项目中常常须要配置知足咱们须要的各类过滤器filter,来过滤知足咱们自定义信息,不烦看一下tomcat中的源码中是如何作到的,下面以tomcat8.5.37
的源码来分析具体实现。
找到ApplicationFilterFactory
类,其中的createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet)
方法建立了责任链,
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; }
执行过滤器中的doFilter
方法,会调用一个 internalDoFilter() 方法
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); } } } }
优势
缺点
注意事项