1.はじめに
Chain of Responsibilityパターン:多个对象
これの要求避け、リクエストを処理する機会发送者
と接受者
の間耦合
の関係を。オブジェクトは、これまでそれを処理するまで、これらの鎖に一緒にオブジェクト、および鎖に沿った要求を渡します。
2.グラフィック
新店舗は、各注文を開け、あなたは救済を重ね合わせた複数のクーポンを楽しむことができます
Chain of Responsibilityパターン
3.ケースの実現
図次のクラス
- 割引クーポンの定義
抽象类
。 - 抽象クラスは、自身への参照が含まれてい
nextDiscountFilter
つなぎ合わせ標的鎖、割引後の価格を計算する元のメソッドに、calculateBySourcePrice
。
実装クラス
FullDistcountFliter
全200 20元。FirstPurchaseDiscount
初めてのバイヤーマイナス20元。SecondPurchaseDiscountFilter
第二部材10%割引。HolidayDiscountFilter
すべての休日マイナス5元。
インタフェースと実装クラス
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
責任パターン手書きアプリケーションフィルタの4チェーン
さて、このようなシーンは、プログラマジョー・スミスが最初で、デートのショーでオブジェクトを検索し、第二のライトをシャットアウトオフ女の子が、それぞれ、アウト合格され、二人の女の子は、ジョー・スミスフィルタされるかを見てみることがあります。
張暁の対話
張Xiaojing対話
彼らは女の子からフィルタリングされているフィルタの条件を満たしていないので、このシナリオでは、各フィルタを介してサーバーへのフィルターを通したhttpリクエストは、それがチェックポイント通過ジョー・スミス日付処理することが考えられるかをシミュレートすることができます。
最初のバージョンV1
- そして、リクエストクラスに応じて、クラスの定義
MyRequest
、MyResponse
。 - フィルタを定義します
MyFilter
。 - クラス2つのフィルタを実装する
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
V1がMyFilterChain
記録オブジェクト添字鎖の特定の責任の付加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);
}
}
結果は、第1の濾過度、濾過高さ表示されています。
request:张三身高170,学历大专,跪求妹子给个机会认识
response:【妹子挑剔,需要过滤学历】【妹子挑剔,需要过滤身高】
以来MyFilterChain
やってdoFilter
、電流が常に渡されているMyFilterChain
同じインスタンス、それを簡略化することができますMyFilterChain
。
3番目のバージョン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:【妹子挑剔,需要过滤学历】【妹子挑剔,需要过滤身高】
5.ソースフィルタ解析
Webプロジェクトでは、多くの場合、我々は次のように、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);
}
}
}
}
責任パターンの概要6.チェーン
利点
- 送信側と受信側のプロセスで区切り要求。リクエスタは、全体像のリクエスト、両方のデカップリングを知っているし、システムの柔軟性を向上させることができない取引を処理しているか分からないかもしれません。
短所
- 比較的長鎖、パフォーマンスは非常に大きな問題である場合は特に、チェーンの最後にチェーンの先頭から各要求を横断するパフォーマンスの問題、。
- デバッグは、特に長鎖、同様の再帰的に、ロジックをデバッグするときには複雑になる可能性が非常に便利ではありません。
注意事項
- 長鎖の発生を回避するために、チェーン内のノードの数を制御する必要があり、一般的にはハンドラ内のノードの最大数を設定することで、しきい値を超えたか否かが判定される、それはチェーンがsetNextプロセスを確立することができない超えて、意図しない避けますシステムのパフォーマンスを破壊します。