当前位置: 首页 > article >正文

Java的责任链模式在项目中的使用

        责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许通过多个处理对象来传递请求,直到一个处理对象处理该请求为止。责任链模式的主要优点是可以解耦请求发送者和请求接收者,客户端无需明确指定哪个对象负责处理请求,只需要将请求传递给链中的第一个处理对象即可。

责任链模式的应用场景:

  1. 请求处理的多个阶段:当一个请求需要多个处理步骤时,每个步骤可以作为责任链中的一个节点进行处理。
  2. 请求处理顺序不固定:请求的处理顺序不固定,具体由系统的需要来决定,通常由责任链中的对象决定是否继续传递请求。
  3. 链中对象可动态添加:责任链中的处理对象可以动态调整。

Spring Web MVC (HandlerInterceptor)

HandlerInterceptor 是一个非常有用的接口,它允许我们在请求到达 Controller 之前和之后,甚至在视图渲染之后执行一些处理。HandlerInterceptor 是基于责任链模式(Chain of Responsibility Pattern)实现的,多个拦截器可以按顺序执行,每个拦截器可以在请求的生命周期中执行不同的任务。

  • preHandle:请求到达 Controller 之前调用,通常用于进行权限检查、日志记录等操作。如果返回 false,请求将终止,后续的拦截器和 Controller 不会被执行。
  • postHandle:请求完成 Controller 执行后,视图渲染之前调用。可以用于在视图渲染之前对模型数据进行处理(例如,修改模型数据或添加属性等)。
  • afterCompletion:视图渲染完成后调用,可以用于清理资源(例如,关闭数据库连接,日志记录等)。
public interface HandlerInterceptor {
    // 在Controller处理请求之前进行调用
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    // 在Controller处理请求之后,视图渲染之前调用
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;

    // 视图渲染完成之后进行调用
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}
如何在 Spring MVC 中使用 HandlerInterceptor
1. 创建自定义的 HandlerInterceptor 实现

首先,我们需要实现 HandlerInterceptor 接口,并重写 preHandlepostHandleafterCompletion 方法。例如:

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {

    // 在Controller之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Pre Handle: Request intercepted before reaching the Controller");
        // 返回true表示继续处理请求,返回false表示请求被拦截,不再传递给Controller
        return true;
    }

    // 在Controller之后,视图渲染之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("Post Handle: Request intercepted after the Controller has finished processing");
    }

    // 视图渲染完成之后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("After Completion: Request has completed, used for cleanup");
    }
}
2. 拦截器链的顺序

如果注册了多个拦截器,拦截器会按照注册顺序执行 preHandle 方法,并且按相反顺序执行 afterCompletion 方法。例如:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
        registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
    }
}

MyInterceptor1preHandle 会在 MyInterceptor2preHandle 之前执行,afterCompletion 会按相反顺序执行。preHandle就组成了一条责任链,afterCompletion又是一条责任链。

常见用途

  1. 权限验证:可以使用 preHandle 检查请求是否具有合法的权限(例如用户是否登录)。
  2. 日志记录:使用 preHandle 记录请求的 URL 和时间,使用 postHandle 记录响应结果。
  3. 性能监控:通过 preHandlepostHandle 计算请求处理的时间。
  4. 修改模型数据:在 postHandle 中修改 ModelAndView,比如添加全局的属性或对模型数据进行修改。
  5. 清理工作:在 afterCompletion 中进行清理工作,比如释放数据库连接、线程池资源等。

开发中责任链的运用

1.校验参数

         在创建商品信息的时候需要去校验很多的各种各样的信息,而且每次需要增加新的条件都需要去修改代码(不符合软件设计的开闭原则):

public void createGoods(GoodsSaveReqDTO requestParam) {
        // 验证请求参数是否正确

        if (ObjectUtil.equal(requestParam.getTarget(),                   DiscountTargetEnum.PRODUCT_SPECIFIC)) {
            // 验证商品是否存在,如果不存在抛出异常
            // ......
        }
        if (StrUtil.isEmpty(requestParam.getName())) {
            throw new ClientException("名称不能为空");
        }
        if (ObjectUtil.isEmpty(requestParam.getSource())) {
            throw new ClientException("来源不能为空");
        }
        //...


        // 新增信息到数据库
        // ...
}
2.用责任链模式来修改上面的代码:
public void createGoods(GoodsSaveReqDTO requestParam) {
        // 通过责任链验证请求参数是否正确
        ChainContext.handler(CREATE_Goods_KEY.name(), requestParam);

        // 信息到数据库
        // ...
}
@Component
public final class ChainContext<T> implements ApplicationContextAware, CommandLineRunner {

    private final Map<String, List<ChainHandler>> ChainHandlerContainer = new HashMap<>();

    /**
     * 责任链组件执行
     *
     * @param mark         责任链组件标识
     * @param requestParam 请求参数
     */
    public void handler(String mark, T requestParam) {
        // 根据 mark 标识从责任链容器中获取一组责任链实现 Bean 集合
        List<ChainHandler> ChainHandlers = ChainHandlerContainer.get(mark);
        ChainHandlers.forEach(each -> each.handler(requestParam));
    }

    @Override
    public void run(String... args) throws Exception {
        // 从 Spring IOC 容器中获取指定接口 Spring Bean 集合
        Map<String, abstractChainHandler> chainFilterMap = applicationContext.getBeansOfType(abstractChainHandler.class);
        chainFilterMap.forEach((beanName, bean) -> {
            // 判断 Mark 是否已经存在抽象责任链容器中,如果已经存在直接向集合新增;如果不存在,创建 Mark 和对应的集合
            List<ChainHandler> ChainHandlers = ChainHandlerContainer.getOrDefault(bean.mark(), new ArrayList<>());
            ChainHandlers.add(bean);
            ChainHandlerContainer.put(bean.mark(), abstractChainHandlers);
        });
        abstractChainHandlerContainer.forEach((mark, unsortedChainHandlers) -> {
            // 对每个 Mark 对应的责任链实现类集合进行排序,优先级小的在前
            unsortedChainHandlers.sort(Comparator.comparing(Ordered::getOrder));
        });
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

要实现具体的handle方法就继承abstractChainHandler

public interface AbstractChainHandler<T> extends Ordered {

    /**
     * 执行责任链逻辑
     *
     * @param requestParam 责任链执行入参
     */
    void handler(T requestParam);

    /**
     * @return 责任链组件标识
     */
    String mark();
}
@Component
public class GoodsCreateParamNotNullChainFilter implements AbstractChainHandler<GoodsSaveReqDTO> {

    @Override
    public void handler(GoodsSaveReqDTO requestParam) {
        if (StrUtil.isEmpty(requestParam.getName())) {
            throw new ClientException("名称不能为空");
        }

        if (ObjectUtil.isEmpty(requestParam.getSource())) {
            throw new ClientException("来源不能为空");
        }

        //...其它条件        
    }

    @Override
    public String mark() {
        return CREATE_Goods_KEY.name();
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

这样就实现了解耦,想要添加新的判断条件,只要再写一个继承abstractChainHandler的类就可以了。


http://www.kler.cn/a/458445.html

相关文章:

  • 静默模式下安装Weblogic 14.1.1.0.0
  • 国产手机嘴上喊着挑战苹果,实际行动却已承认失败,真的干不过
  • 2024年中国新能源汽车用车发展怎么样 PaperGPT(二)
  • webpack打包node后端项目
  • 常见端口(22、25、53、80、443、110、143、3306、6379、21)和服务的安装与配置手册
  • Anaconda+PyTorch(CPU版)安装
  • 如何优化求职简历从模板选择到面试准备
  • LeetCode 203:根据值删除节点
  • HDLBits训练6
  • Java爬虫实战:获取亚马逊商品详情
  • 五.Springboot通过AOP实现API接口的签名验证
  • Go IO之文件处理,TCPUDP讲解
  • CF2043b-B. Digits
  • ASP.NET Core Web API Hangfire
  • C# OpenCV机器视觉:漫水填充
  • 春招快速准备和是否考研建议
  • 深度学习实战102-基于深度学习的网络入侵检测系统,利用各种AI模型和pytorch框架实现网络入侵检测
  • STM32高级 以太网通讯案例1:网络搭建(register代码)
  • leetcode 面试经典 150 题:删除有序数组中的重复项
  • 基于SSM的“一汽租车辆共享平台”的设计与实现(源码+数据库+文档+PPT)
  • vue-复制剪贴板
  • pytorch整体环境打包安装到另一台电脑上
  • 高级技巧-使用Mysql 实现根据条件过滤整个分组数据
  • 正则化强度的倒数C——让模型学习更准确
  • Bash 脚本教程
  • 【Python】什么是元组(Tuple)?