由浅入深讲解责任链模式,理解Tomcat的Filter过滤器
本文将从简单的场景引入, 逐步优化, 最后给出具体的责任链设计模式实现.
场景引入
- 首先我们考虑这样一个场景: 论坛上用户要发帖子, 但是用户的想法是丰富多变的, 他们可能正常地发帖, 可能会在网页中浅入html代码, 可能会使用错误的表情格式, 也可能发送一些敏感信息.
- 作为论坛的管理员必须对用户的帖子进行过滤才能显示出来, 否则论坛就经营不下去了. 现在我们考虑一种最简单处理方式.
public class Demo1 {
public static void main(String[] args) {
String msg = "大家好 :), <script>haha</script> 我要说超级敏感的话";//假设有一条这样的贴子
MsgProcessor mp = new MsgProcessor();
mp.setMsg(msg);//处理帖子
System.out.println(mp.process());
}
}
//帖子处理器
class MsgProcessor{
private String msg;
public String process(){
//对html标签<>进行处理
String str = msg.replace("<", "[").replace(">", "]");
//对敏感字符尽心处理
str = str.replace("敏感", "正常");
//对错误的表情格式进行处理
str = str.replace(":)", "^_^");
return str;
}
//get() / set() 方法...
}
//输出结果
大家好 ^_^, [script]haha[/script] 我要说超级正常的话
责任链模型初体现
- 通过上面的代码可以看到帖子处理器会对帖子进行不同的过滤, 我们可以把一种过滤方法对应为一个过滤器, 并且向上抽取出过滤器接口.
public class Demo2 {
public static void main(String[] args) {
String msg = "大家好 :), <script>haha</script> 我要说超级敏感的话";
MsgProcessor mp = new MsgProcessor();
mp.setMsg(msg);
System.out.println(mp.process());
}
}
class MsgProcessor{
private String msg;
private Filter[] filters = {new HtmlFilter(), new SensitiveFilter(), new ExpressionFilter()};
public String process(){
for(Filter f : filters){
msg = f.doFilter(msg);
}
return msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
//过滤器接口
interface Filter{
public String doFilter(String s);
}
//处理html标签
class HtmlFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("<", "[").replace(">", "]");
}
}
//处理敏感词句
class SensitiveFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("敏感", "正常");
}
}
//处理表情
class ExpressionFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace(":)", "^_^");
}
}
- 上面的代码已经具备了责任链的模型. 在帖子发送到服务器的过程中, 它将依次经过3个过滤器, 这三个过滤器就构成一条过滤器链.
- 下面我们考虑, 如果我们要在帖子处理过程中加入新的过滤器链条, 加在原链条的末尾或中间, 该怎么办呢?
- 消息经过过滤器链条的过程会得到处理, 我们可以把过滤器链条看成一个过滤器, 让他也实现
Filter
接口, 那么就可以在一条过滤链中任意加入其他过滤器和过滤链了. - 下面的代码实现了过滤链
FilterChain
, 用过滤链替代原来的MsgProcessor
.
public class Demo3 {
public static void main(String[] args) {
String msg = "大家好 :), <script>haha</script> 我要说超级敏感的话";//待处理的帖子
FilterChain fc1 = new FilterChain();//创建一条过滤器链1
fc1.add(new HtmlFilter())
.add(new SensitiveFilter());//往过滤器链1中添加过滤器
FilterChain fc2 = new FilterChain();//创建一条过滤器链2
fc2.add(new ExpressionFilter());//往过滤器链2中添加过滤器
fc1.add(fc2);//把过滤器链2当作过滤器添加到过滤器链1中,(过滤器链实现了Filter接口)
msg = fc1.doFilter(msg);//使用过滤器链1对帖子进行过滤
System.out.println(msg);
}
}
class FilterChain implements Filter{
private List<Filter> list = new ArrayList<>();
public FilterChain add(Filter filter){
this.list.add(filter);
return this;
}
@Override
public String doFilter(String s) {
for(Filter f : list){
s = f.doFilter(s);
}
return s;
}
}
class HtmlFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("<", "[").replace(">", "]");
}
}
class SensitiveFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace("敏感", "正常");
}
}
class ExpressionFilter implements Filter{
@Override
public String doFilter(String s) {
return s.replace(":)", "^_^");
}
}
interface Filter{
public String doFilter(String s);
}
更精巧设计, 展现责任链模式
- 在继续优化之前, 我们考虑更现实的需求, 一个请求(发出一个帖子)作为数据报发送给服务器, 服务器除了需要对请求进行过滤外, 还需要给出响应, 并且可能要对响应也进行处理. 如下图所示
- 当一个消息(包含请求体和响应体)发往服务器时, 它将依次经过过滤器1, 2, 3. 而当处理完成后, 封装好响应发出服务器时, 它也将依次经过过滤器3, 2, 1.
- 大家可能会觉得有点像栈结构, 但是像归像, 这一逻辑应该如何实现呢?
- 首先我们可以让过滤器持有过滤器链的引用, 通过调用过滤器链依次执行每个过滤器. 为了能让过滤器依次执行每个过滤器, 过滤器会持有一个
index
序号, 通过序号控制执行顺序. 至于后面对response
的倒序请求, 则通过方法返回实现. 这部分设计纯用文字难以讲清, 请务必看下面的代码和代码后的分析, 配图. - 这个部分是责任链的精髓了, 懂了这部分代码, 看Web开发中的过滤器源码就没压力了.
public class Demo4 {
public static void main(String[] args) {
String msg = "大家好 :), <script>haha</script> 我要说超级敏感的话";//以下三行模拟一个请求
Request request = new Request();
request.setRequestStr(msg);
Response response = new Response();//响应
FilterChain fc = new FilterChain();//过滤器链
HtmlFilter f1 = new HtmlFilter();//创建过滤器
SensitiveFilter f2 = new SensitiveFilter();
ExpressionFilter f3 = new ExpressionFilter();
fc.add(f1);//把过滤器添加到过滤器链中
fc.add(f2);
fc.add(f3);
fc.doFilter(request, response, fc);//直接调用过滤器链的doFilter()方法进行处理
System.out.println(request.getRequestStr());
}
}
interface Filter{
public void doFilter(Request request, Response response, FilterChain fc);
}
class FilterChain implements Filter{
private List<Filter> list = new ArrayList<>();
private int index = 0;
public FilterChain add(Filter filter){
this.list.add(filter);
return this;
}
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
if(index == list.size()){
return;//这里是逆序处理响应的关键, 当index为容器大小时, 证明对request的处理已经完成, 下面进入对response的处理.
}
Filter f = list.get(index);//过滤器链按index的顺序拿到filter
index++;
f.doFilter(request, response, fc);
}
}
class HtmlFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace("<", "[").replace(">","]"));
System.out.println("在HtmlFilter中处理request");//先处理request
fc.doFilter(request, response, fc);//调用过滤器链的doFilter方法, 让它去执行下一个Filter的doFilter方法, 处理response的代码将被挂起
System.out.println("在HtmlFilter中处理response");
}
}
class SensitiveFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace("敏感", "正常"));
System.out.println("在SensitiveFilter中处理request");
fc.doFilter(request, response, fc);
System.out.println("在SensitiveFilter中处理response");
}
}
class ExpressionFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace(":)", "^_^"));
System.out.println("在ExpressionFilter中处理request");
fc.doFilter(request, response, fc);
System.out.println("在ExpressionFilter中处理response");
}
}
class Request{
private String requestStr;//真正的Request对象中是包含很多信息的, 这里仅用一个字符串作模拟
public String getRequestStr() {
return requestStr;
}
public void setRequestStr(String requestStr) {
this.requestStr = requestStr;
}
}
class Response{
private String responseStr;
public String getResponseStr() {
return responseStr;
}
public void setResponseStr(String responseStr) {
this.responseStr = responseStr;
}
}
- 下面我描述一次整个过程, 你可以根据文字找到相应的代码进行理解.
- 首先我们分别创建一个
Request
和Response
对象.Request
在传入进后端时需要依次被过滤器1, 2, 3进行处理,Response
对象在输出时要依次被过滤器3, 2, 1处理. - 创建好请求和响应对象后我们创建过滤器链, 并依次加入过滤器1, 2, 3. 整个处理流程将交给过滤器链决定.
- 接着我们调用过滤器链的
doFilter()
方法对request对象进行处理 - 这时过滤器链中的
index
值为0, 通过index
我们找到第一个过滤器并调用它的doFilter()
方法, 我们观察这段代码
class HtmlFilter implements Filter{
@Override
public void doFilter(Request request, Response response, FilterChain fc) {
request.setRequestStr(request.getRequestStr().replace("<", "[").replace(">","]"));
System.out.println("在HtmlFilter中处理request");//先处理request
fc.doFilter(request, response, fc);//调用过滤器链的doFilter方法, 让它去执行下一个Filter的doFilter方法, 处理response的代码将被挂起
//在返回的过程中执行response
System.out.println("在HtmlFilter中处理response");
}
}
- 进入
doFilter()
方法后, 首先会对request
请求进行处理, 然后又调用了过滤器链的doFilter()
方法. 这就是整个责任链模式的精妙之处, 它解释了为什么要给doFilter()
加上一个过滤器链参数, 就是为了让每个过滤器可以调用过滤器链本身执行下一个过滤器. - 为什么要调用过滤器链本身? 因为当调用过滤器本身后, 程序将跳转回到过滤器链的
doFilter
方法执行, 这时index
为1, 也就是拿到第二个过滤器, 然后继续处理. - 正是由于这个跳转, 使得过滤器中对
response
的处理暂时无法执行, 它必须等待上面的对过滤器链的方法返回才能被执行. - 所以最后我们将看到
response
响应被过滤器3, 2, 1(和请求倒序)执行.
- 放大招了, 如果看了上面的图还是不懂, 欢迎给我留言.
- 整个责任链模式已经从无到有展现出来了
阅读Tomcat中的Filter过滤器源码, 加深理解.
- 相信通过上面的讲解, 你已经对整个责任链模式有了进一步的理解.
- 下面我们通过阅读Tomcat里Filter的源码感受一下.
public interface Filter {
void init(FilterConfig var1) throws ServletException;
//熟悉的doFilter(), 熟悉的3个参数request, reponse, filterChain.
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
void destroy();
}
- 我们可以看到
Filter
接口的定义和我们的讲解的差不多, 只是多了初始化和销毁的方法.
public interface FilterChain {
void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
- 同时我们也看到它把
FilterChain
也向上抽取成接口, 不过这里的FilterChain
没有实现Filter
接口, 也就是说我们不能把两条FilterChain
拼接在一起, 换个角度想Tomcat中的过滤器的可扩展性还没有我们例子中的好呢^_^
由浅入深讲解责任链模式,理解Tomcat的Filter过滤器的更多相关文章
- 设计模式——责任链(结合Tomcat中Filter机制)
设计模式:责任链模式 说责任链之前,先引入一个场景,假如规定学生请假小于或等于 2 天,班主任可以批准:小于或等于 7 天,系主任可以批准:小于或等于 10 天,院长可以批准:其他情况不予批准:以此为 ...
- 设计模式学习笔记(十四)责任链模式实现以及在Filter中的应用
责任链模式(Chain Of Responsibility Design Pattern),也叫做职责链,是将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求.当有请求发生时,可将请求沿着这条 ...
- 基于【 责任链模式】二 || 网关zuul过滤器封装
一.基于责任链模式封装网关拦截 上一篇文章中已经使用建造者模式对网关拦截进行封装,存在一个问题,在连接器build中,每一个拦截都要进行true判断,代码看起来冗余,下面使用责任链模式封装 1.基于责 ...
- Java设计模式系列之责任链模式
责任链模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道 ...
- JAVA设计模式之责任链模式
在阎宏博士的<JAVA与模式>一书中开头是这样描述责任链(Chain of Responsibility)模式的: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其 ...
- 设计模式《JAVA与模式》之责任链模式
在阎宏博士的<JAVA与模式>一书中开头是这样描述责任链(Chain of Responsibility)模式的: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其 ...
- 设计模式(13)--Chain of Responsibility(责任链模式)--行为型
作者QQ:1095737364 QQ群:123300273 欢迎加入! 1.模式定义: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一 ...
- Java设计模式の责任链模式
在阎宏博士的<JAVA与模式>一书中开头是这样描述责任链(Chain of Responsibility)模式的: 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其 ...
- java设计模式之责任链模式(Chain of Responsibility)
转自:http://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html 在阎宏博士的<JAVA与模式>一书中开头是这样 ...
随机推荐
- django优化和扩展(一)
mysql优化基础 进行django产品开发或上线之前,有必要了解一下mysql的基础知识,orm太过抽象,导致很多朋友对于mysql了解得太少,而且orm不像sqlalchemy那样可以跟mysql ...
- PAT1118:Birds in Forest
1118. Birds in Forest (25) 时间限制 150 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Some ...
- 注解@PostConstruct与@PreDestroy 特性说明
简介 Java EE5 引入了@PostConstruct和@PreDestroy这两个作用于Servlet生命周期的注解,实现Bean初始化之前和销毁之前的自定义操作.此文主要说明@PostCons ...
- 解决fatal error LNK1168的终极方法
很多人的VC++或Visual studio 会出现fatal error LNK1168错误很是头疼,MS也说不清, 什么改权限.用户名.注册表.CMD,卸载杀毒软件...一切都瞎扯,除非reins ...
- spring security oauth2
https://connect.qq.com/manage.html#/ http://wiki.connect.qq.com/%E7%BD%91%E7%AB%99%E5%BA%94%E7%94%A8 ...
- LINUX PID 1和SYSTEMD 专题
Linux下有3个特殊的进程,idle进程(PID = 0), init进程(PID = 1)和kthreadd(PID = 2) idle进程其pid=0,其前身是系统创建的第一个进程,也是唯一一个 ...
- jQuery学习之旅 Item4 细说DOM操作
jQuery-–DOM操作(文档处理) Dom是Document Object Model的缩写,意思是文档对象模型.DOM是一种与浏览器.平台.语言无关的接口,使用该接口可以轻松访问页面中所有的标准 ...
- yii2.0 路由美化以及自定义设置
在配置文件里修改: 如果把'enablePrettyUrl' => true,设为true那么你的gii就访问不到: 'rules' => [ '<c ...
- java中Collection容器
1.容器(Collection)也称为集合, 在java中就是指对象的集合. 容器里存放的都只能是对象. 实际上是存放对象的指针(头部地址): 这里对于八种基本数据类型,在集合中实际存的是对应的包装类 ...
- System.out.println()
System是java.lang中的类,out为System中的一个静态数据成员,out是java.io.PrintStream类的对象,而println()是java.io.PrintStream类 ...