目录

设计模式-责任链模式

原理和实现

将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收 对象能够处理它为止,实时上,在常见的使用场景中,我们的责任链并不是和概念中的完全一样

● 原始概念中,是直到链上的某个接收对象能够处理它为止

● 实际使用中,链上的所有对象都可以对请求进行特殊处理

实现方式

使用链表实现

  1. 定义虚拟类Handler
java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public abstract class Handler {
    protected Handler successor = null;
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }
    public abstract void handle();
}
  1. 定义具体处理器
java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public class HandlerA extends Handler{
    @Override
    public void handle() {
        boolean handled = false;
//...
        if (!handled && successor != null) {
            successor.handle();
        }
    }
}


package com.hillky.desgin_learn.chainResponsibility;

public class HandlerB extends Handler{
    @Override
    public void handle() {
        boolean handled = false;
//...
        if (!handled && successor != null) {
            successor.handle();
        }
    }
}
  1. 创建HandlerChain
java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public class HandlerChain {
    private Handler head = null;
    private Handler tail = null;

    public void addHandler(Handler handler) {
        handler.setSuccessor(null);
        if (head == null) {
            head = handler;
            tail = handler;
            return;
        }
        tail.setSuccessor(handler);
        tail = handler;
    }
    public void handle() {
        if (head != null) {
            head.handle();
        }
    }
}
  1. 使用测试
java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public class Client {
    public static void main(String[] args) {
        HandlerChain chain = new HandlerChain();
        chain.addHandler(new HandlerA());
        chain.addHandler(new HandlerB());
        chain.handle();
    }
}

处理器类的 handle() 函数,不仅包含自己的业 务逻辑,还包含对下一个处理器的调用,也就是代码中的 successor.handle()。一个不熟悉这种代码结构的程序员,在添加新的处理器类的时候,很有可能忘记在handle() 函数中调用 successor.handle(),这就会导致代码出现 bug。

针对这个问题,我们对代码进行重构,利用模板模式,将调用 successor.handle() 的逻辑从具体的处理器类中剥离出来,放到抽象父类中。

java 复制代码
package com.hillky.desgin_learn.chainResponsibility;

public abstract class Handler {

    protected Handler successor = null;
    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public abstract boolean doHandle();

    public final void handle() {
        boolean handled = doHandle();
        if (successor != null && !handled) {
            successor.handle();
        }
    }
}

package com.hillky.desgin_learn.chainResponsibility;

public class HandlerA extends Handler{

    @Override
    public boolean doHandle() {
        boolean handled = false;
        System.out.println("Handler A在处理");
//...
        return handled;
    }
}

使用数组实现

这种实现方式更加简单。 HandlerChain 类用数组而非链表来保存所有的处理器,并且需要在 HandlerChain 的 handle() 函数中,依次调用每个处理器的 handle() 函数。

  1. Handler接口
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.array;

public interface IHandler {
    boolean handle();
}
  1. Handler具体实现
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.array;

import javax.xml.ws.handler.Handler;

public class HandlerA implements IHandler {
    @Override
    public boolean handle() {
        boolean handled = false;
        System.out.println("HandlerA 处理");
        return handled;
    }
}


package com.hillky.desgin_learn.chainResponsibility.array;

public class HandlerB implements IHandler {
    @Override
    public boolean handle() {
        boolean handled = false;
        System.out.println("HandlerB 处理");
        return handled;
    }
}
  1. HandlerChain
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.array;

import java.util.ArrayList;
import java.util.List;

public class HandlerChain {
    private List<IHandler> handlers = new ArrayList<>();

    public void addHandler(IHandler handler){
        handlers.add(handler);
    }

    public void handle() {
        for (IHandler handler : handlers) {
            boolean handled = handler.handle();
            if (handled) {
                break;
            }
        }
    }
}
  1. 测试
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.array;
public class Client {
    public static void main(String[] args) {
        HandlerChain chain = new HandlerChain();
        chain.addHandler(new HandlerA());
        chain.addHandler(new HandlerB());
        chain.handle();
    }
}

源码实现

Servlet Filter

Servlet Filter 是 Java Servlet 规范中定义的组件 ,翻译成中文就是过滤器,它可以实 现对 HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。因为它是 Servlet 规范的一部分,所以,只要是支持 Servlet 的 Web 容器(比如,Tomcat、 Jetty 等),都支持过滤器功能。

可以自定义实现过滤器配置到web中(chatgpt搜索实现)

简单的示例

  1. 创建一个名为 RequestTimingFilter 的 Java 类,实现 javax.servlet.Filter 接口
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class RequestTimingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 在请求到达时记录开始时间
        long startTime = System.currentTimeMillis();
        // 将请求传递给责任链上的下一个 Filter 或目标 Servlet
        chain.doFilter(request, response);
        // 在请求处理完成后记录结束时间,并计算耗时
        long endTime = System.currentTimeMillis();
        long timeElapsed = endTime - startTime;

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String requestURI = httpServletRequest.getRequestURI();
        System.out.println("请求 " + requestURI + " 的访问耗时:" + timeElapsed + " 毫秒");

    }
}
  1. 注册到我们的web工程中(添加注解)
java 复制代码
package com.hillky.desgin_learn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
@ServletComponentScan // 扫描Servlet组件
public class DesginLearnApplication {

    public static void main(String[] args) {
        SpringApplication.run(DesginLearnApplication.class, args);
    }

}
package com.hillky.desgin_learn.chainResponsibility.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class RequestTimingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 在请求到达时记录开始时间
        long startTime = System.currentTimeMillis();
        // 将请求传递给责任链上的下一个 Filter 或目标 Servlet
        chain.doFilter(request, response);
        // 在请求处理完成后记录结束时间,并计算耗时
        long endTime = System.currentTimeMillis();
        long timeElapsed = endTime - startTime;

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String requestURI = httpServletRequest.getRequestURI();
        System.out.println("请求 " + requestURI + " 的访问耗时:" + timeElapsed + " 毫秒");

    }
}

Spring Interceptor

刚刚讲了 Servlet Filter,现在我们来讲一个功能上跟它非常类似的东西,Spring Interceptor,翻译成中文就是拦截器。尽管英文单词和中文翻译都不同,但这两者 基本上可以看作一个概念,都用来实现对 HTTP 请求进行拦截处理。

它们不同之处在于,Servlet Filter 是 Servlet 规范的一部分,实现依赖于 Web 容 器。Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提 供实现。客户端发送的请求,会先经过 Servlet Filter,然后再经过 Spring Interceptor,最后到达具体的业务代码中。

简单的示例

  1. 定义Interceptor
java 复制代码
public class RequestTimingInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求到达时记录开始时间
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);

        // 返回 true 以将请求传递给责任链上的下一个 Interceptor 或目标 Controller
        return true;
//        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在请求处理完成后记录结束时间,并计算耗时
        long endTime = System.currentTimeMillis();
        long startTime = (Long) request.getAttribute("startTime");
        long timeElapsed = endTime - startTime;
        String requestURI = request.getRequestURI();
        System.out.println("请求 " + requestURI + " 的访问耗时:" + timeElapsed + "毫秒");
    }
}
  1. 在你的 Spring 配置类中,注册并配置这个 Interceptor
java 复制代码
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestTimingInterceptor());
    }
}
  1. 当你的 Spring 应用收到 HTTP 请求时, RequestTimingInterceptor 将会拦截这个请求,并在请求处理前后记录时间。

应用场景

  1. 日志记录器:在应用程序中,我们可能需要将日志记录到不同的位置,如控制 台、文件、数据库等。我们可以创建一个日志记录器链,每个记录器处理特定级别的日志,然后将请求传递给下一个记录器。这样,可以根据日志级别灵活地记录日志信息。
  2. Web 应用中的过滤器和拦截器:在 Web 应用程序中,我们经常需要对请求进行 预处理和后处理,如身份验证、授权、编码转换、请求日志记录等。过滤器和拦截器就是典型的使用责任链模式的场景,请求和响应在过滤器或拦截器链中依次传递,每个过滤器或拦截器执行特定的任务。
  3. 工作流引擎:在一个工作流引擎中,一个请求可能需要经过多个处理步骤,这些步骤可以看作是一个责任链。每个处理器处理请求的一个部分,然后将请求传递给下一个处理器,直到请求被完全处理。

实践

对于支持 UGC(User Generated Content,用户生成内容)的应用(比如论坛)来 说,用户生成的内容(比如,在论坛中发表的帖子)可能会包含一些敏感词(比如涉 黄、广告、反动等词汇)。针对这个应用场景,我们就可以利用职责链模式来过滤这 些敏感词。 过滤敏感词

在这个应用场景中,我们可以创建一个过滤器链来过滤用户生成的内容。每个过滤器 负责处理一种类型的敏感词,然后将内容传递给下一个过滤器。以下是一个简单的实现示例:

  1. 定义一个过滤器接口
java 复制代码
public interface ContentFilter {
    String filter(String content);
}
  1. 实现不同类型的过滤器,例如涉黄过滤器、广告过滤器和反动过滤器
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.ugc;

public class PornographyFilter implements ContentFilter{

    @Override
    public String filter(String content) {
        // 这里用简单的字符串替换来表示过滤操作,实际应用中需要更复杂的过滤逻辑
        return content.replaceAll("涉黄词汇", "***");
    }

}

package com.hillky.desgin_learn.chainResponsibility.ugc;

// 广告过滤器
public class AdvertisementFilter implements ContentFilter {
    @Override
    public String filter(String content) {
        return content.replaceAll("广告词汇", "***");
    }
}

package com.hillky.desgin_learn.chainResponsibility.ugc;

// 反动过滤器
public class ReactionaryFilter implements ContentFilter {
    @Override
    public String filter(String content) {
        return content.replaceAll("反动词汇", "***");
    }
}
  1. 创建一个过滤器链
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.ugc;

import java.util.ArrayList;
import java.util.List;

public class FilterChain {
    private List<ContentFilter> filters=new ArrayList<>();

    public FilterChain addFilter(ContentFilter filter) {
        filters.add(filter);
        return this;
    }

    public String doFilter(String content) {
        for (ContentFilter filter : filters) {
            content = filter.filter(content);
        }
        return content;
    }
}
  1. 测试
java 复制代码
package com.hillky.desgin_learn.chainResponsibility.ugc;

public class Client {
    public static void main(String[] args) {
        // 创建一个过滤器链
        FilterChain filterChain = new FilterChain();
        filterChain.addFilter(new PornographyFilter())
        .addFilter(new AdvertisementFilter())
        .addFilter(new ReactionaryFilter());
        // 用户生成的内容
        String userContent = "这里有一些涉黄词汇,这里有一些广告词汇,这里有一些反动词汇。";
        // 使用过滤器链处理内容
        String filteredContent = filterChain.doFilter(userContent);
        System.out.println(filteredContent);
    }
}
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
RainbowSea1 分钟前
6. RabbitMQ 死信队列的详细操作编写
java·消息队列·rabbitmq
RainbowSea9 分钟前
5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
java·消息队列·rabbitmq
李少兄2 小时前
Unirest:优雅的Java HTTP客户端库
java·开发语言·http
此木|西贝2 小时前
【设计模式】原型模式
java·设计模式·原型模式
可乐加.糖2 小时前
一篇关于Netty相关的梳理总结
java·后端·网络协议·netty·信息与通信
s9123601012 小时前
rust 同时处理多个异步任务
java·数据库·rust
9号达人2 小时前
java9新特性详解与实践
java·后端·面试
cg50172 小时前
Spring Boot 的配置文件
java·linux·spring boot
啊喜拔牙2 小时前
1. hadoop 集群的常用命令
java·大数据·开发语言·python·scala
anlogic3 小时前
Java基础 4.3
java·开发语言