本文将从简单的场景引入, 逐步优化, 最后给出具体的责任链设计模式实现.
场景引入
首先我们考虑这样一个场景: 论坛上用户要发帖子, 但是用户的想法是丰富多变的, 他们可能正常地发帖, 可能会在网页中浅入 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 中的过滤器的可扩展性还没有我们例子中的好呢 ^_^
来源: https://www.cnblogs.com/tanshaoshenghao/p/10741160.html