马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
责任链模式(Chain of Responsibility Pattern)是一种举动计划模式,它允许通过多个处理处罚对象来转达哀求,直到一个处理处罚对象处理处罚该哀求为止。责任链模式的告急长处是可以解耦哀求发送者和哀求吸收者,客户端无需明确指定哪个对象负责处理处罚哀求,只须要将哀求转达给链中的第一个处理处罚对象即可。
责任链模式的应用场景:
- 哀求处理处罚的多个阶段:当一个哀求须要多个处理处罚步调时,每个步调可以作为责任链中的一个节点举行处理处罚。
- 哀求处理处罚次序不固定:哀求的处理处罚次序不固定,详细由体系的须要来决定,通常由责任链中的对象决定是否继续转达哀求。
- 链中对象可动态添加:责任链中的处理处罚对象可以动态调解。
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 接口,并重写 preHandle、postHandle 和 afterCompletion 方法。比方:- 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("/**");
- }
- }
复制代码 MyInterceptor1 的 preHandle 会在 MyInterceptor2 的 preHandle 之前实验,afterCompletion 会按相反次序实验。preHandle就构成了一条责任链,afterCompletion又是一条责任链。
常见用途
- 权限验证:可以使用 preHandle 查抄哀求是否具有正当的权限(比方用户是否登录)。
- 日志
记录:使用 preHandle 记录哀求的 URL 和时间,使用 postHandle 记录相应结果。
- 性能监控
:通过 preHandle 和 postHandle 盘算哀求处理处罚的时间。
- 修改模子数据:在 postHandle 中修改 ModelAndView,比如添加全局的属性或对模子数据举行修改。
- 整理工作:在 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的类就可以了。
|