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

设计模式之拦截器模式

目录

1.概述

2.结构

3.java实现示例

4.常见实现框架

5.C++实现拦截器模式

6.拦截器和过滤器的异同

7.应用场景

8.优缺点

9.总结


1.概述

        拦截器模式(Interceptor Pattern)是一种在请求被处理之前或之后自动执行代码的设计模式。它允许开发者在方法调用之前或之后插入自定义行为,而无需修改原有的方法实现。这种设计模式在多种编程语言和框架中广泛使用,特别是在Web开发和企业级应用架构中。比如:服务调用的拦截处理,拦截服务的参数,参数国际化处理,拦截服务的异常,记录服务的调用结果等等。

        它的主要特点有:

        透明性:拦截器对于请求处理流程中的其他组件是透明的,即它们不需要知道拦截器的存在。

        灵活性:可以轻松地添加、删除或修改拦截器,而不需要修改被拦截的代码。

        重用性:拦截器可以被多个组件或请求重用,减少了代码冗余。

        关注点分离:拦截器允许将横切关注点(如日志记录、事务管理、安全检查等)从业务逻辑中分离出来,提高了代码的模块化和可维护性。

        它的原理如下图所示:

2.结构

        拦截器设计模式的UML图如下所示:

        主要角色有:
        1)Interceptor:拦截器接口,负责在业务逻辑执行前后插入自定义行为的组件。
        2)InterceptorImpl:拦截器实现,用来在Target处理请求前后做切面处理。
        3)TargetInvocation:调度器/拦截器链负责管理和调度拦截器的组件。调度器按照一定的顺序将请求传递给拦截器链中的每个拦截器,并控制请求的最终处理。这里包含了一组Interceptor和一个Target对象,确保在Target处理请求前后,按照定义顺序调用Interceptor做前置和后置处理。
        4)Target:被拦截器拦截并处理的请求的最终目标。目标对象可以是任何类型的处理单元,如控制器、服务层组件等。这里指处理请求的目标接口。

3.java实现示例

创建Target接口

public interface Target{ public Response execute(Request request); 
}

创建Interceptor接口

public interface Interceptor { public Response intercept(TargetInvocation targetInvocation); 
}

创建TargetInvocation

public class TargetInvocation {    private List<Interceptor> interceptorList = new ArrayList<>();    private Iterator<Interceptor> interceptors;    private Target target; private Request request;    public Response invoke(){        if( interceptors.hasNext() ){       //此处是整个算法的关键,这里会递归调用invoke()        Interceptor interceptor = interceptors.next();      interceptor.intercept(this);       }         return target.execute(request);     }     public void addInterceptor(Interceptor interceptor){         //添加新的Interceptor到TargetInvocation中         interceptorList.add(interceptor);         interceptors = interceptorList.iterator();     } 
}

创建具体的Interceptor

AuditInterceptor实现如下:

public class AuditInterceptor implements Interceptor{ @Override public Response intercept(TargetInvocation targetInvocation) {                                 if(targetInvocation.getTarget() == null) { throw new IllegalArgumentException("Target is null"); } System.out.println("Audit Succeeded "); return targetInvocation.invoke(); } 
}

LogInterceptor实现如下:

public class LogInterceptor implements Interceptor { @Override public Response intercept(TargetInvocation targetInvocation) { System.out.println("Logging Begin"); Response response = targetInvocation.invoke(); System.out.println("Logging End"); return response; }}

InterceptorDemo演示:

public class InterceptorDemo { public static void main(String[] args) { TargetInvocation targetInvocation = new TargetInvocation(); targetInvocation.addInterceptor(new LogInterceptor()); targetInvocation.addInterceptor(new AuditInterceptor()); targetInvocation.setRequest(new Request()); targetInvocation.setTarget(request->{return new Response();});targetInvocation.invoke();}}

输出:

Logging Begin
Audit Succeeded
Logging End

4.常见实现框架

        拦截器的实现方式因语言和框架而异,但基本思想相似。以下是一些常见语言和框架中拦截器的实现方式:

  • Java(Spring框架):Spring AOP(面向切面编程)提供了拦截器的功能,通过定义切面(Aspect)和通知(Advice)来实现。
  • Python(Flask框架):Flask通过中间件(Middleware)来提供拦截器的功能,中间件可以注册在请求处理流程的不同阶段。
  • JavaScript(Node.js/Express框架):Express通过中间件函数来提供拦截器的功能,中间件函数可以访问请求对象(req)、响应对象(res)和应用程序的请求/响应循环中的下一个中间件函数。

5.C++实现拦截器模式

        在C++中,并没有像一些高级框架(如Spring)那样内置的直接支持“拦截器模式”的机制。但是,你可以通过一些设计模式和编程技巧来模拟拦截器的行为。

        拦截器模式的核心思想是在方法调用之前和之后自动执行特定的代码。在C++中,这通常可以通过以下几种方式实现:

1)函数包装(Function Wrappers)
        你可以创建一个函数或函数对象,它接受一个函数指针或可调用对象作为参数,并在调用这个函数之前和之后执行特定的代码。这类似于高阶函数的概念。

2)装饰器模式(Decorator Pattern)
        虽然装饰器模式主要用于在运行时动态地给一个对象添加一些额外的职责,但它也可以用来模拟拦截器的行为。通过创建一个包装类(Decorator),该类在其内部持有对原始对象的引用,并在调用原始对象的任何方法之前和之后执行拦截逻辑。

3)中间件(Middleware)
        在Web服务器或网络应用程序中,中间件是一种常见的拦截器实现方式。虽然C++不直接支持像Node.js那样的中间件模型,但你可以通过设计自己的中间件架构来实现类似的功能。

4)AOP(面向切面编程)
        虽然C++标准库本身不支持AOP,但你可以使用第三方库(如AspectC++)或手动实现AOP的某些方面。AOP允许你在不修改源代码的情况下增加新的行为(即“切面”),这非常类似于拦截器的概念。

5)智能指针和析构函数
        在某些情况下,你可以使用智能指针(如std::unique_ptrstd::shared_ptr)和自定义的析构函数来在对象生命周期结束时执行拦截逻辑。虽然这通常不是拦截器模式的典型用法,但它可以在特定场景下发挥作用。

示例:使用函数包装来模拟拦截器

#include <iostream>  
#include <functional>  template<typename Func, typename... Args>  
void intercept(Func func, Args&&... args) {  std::cout << "Before function call" << std::endl;  func(std::forward<Args>(args)...);  std::cout << "After function call" << std::endl;  
}  void myFunction(int x) {  std::cout << "Function called with argument: " << x << std::endl;  
}  int main() {  intercept(myFunction, 42);  return 0;  
}

        在这个例子中,intercept函数是一个模板函数,它接受一个可调用对象(如函数指针、lambda表达式或函数对象)和任意数量的参数。在调用这个可调用对象之前和之后,它分别打印了“Before function call”和“After function call”。这模拟了在方法调用之前和之后自动执行代码的行为。

        请注意,这个例子非常基础,并且没有涵盖拦截器可能需要的所有复杂性和灵活性。在实际应用中,你可能需要根据具体情况调整和设计更复杂的拦截器实现。

6.拦截器和过滤器的异同

        拦截器是一种动态拦截方法调用的机制,主要用于拦截用户请求并在请求处理的不同阶段执行一些操作,如日志记录、权限检查、事务管理等。在Spring MVC等框架中,拦截器通过实现特定的接口(如HandlerInterceptor)来定义,并在框架的配置中注册和使用。

        过滤器是一种能够处理HTTP请求和响应的Web组件,它在请求到达Servlet之前或响应发送给客户端之后执行。过滤器通过实现javax.servlet.Filter接口来定义,并在web.xml配置文件或通过注解方式注册到Servlet容器中。

        下面是它们的区别:

拦截器(Interceptor)过滤器(Filter)
定义与功能动态拦截方法调用的机制,用于在请求处理的不同阶段执行操作处理HTTP请求和响应的Web组件,在请求到达Servlet之前或响应发送给客户端之后执行
实现方式实现特定接口(如HandlerInterceptor),在框架配置中注册实现javax.servlet.Filter接口,在web.xml中或通过注解注册
作用范围主要作用于Controller层的方法调用,不能拦截静态资源请求等可以拦截几乎所有的请求,包括静态资源和非静态资源的请求
执行时机在方法执行前后、异常抛出前后等阶段进行拦截在请求到达Servlet之前或响应发送给客户端之后执行
配置方式在框架的配置文件中注册(如Spring MVC的Java配置或XML配置)在web.xml中配置或通过注解配置
访问能力可以访问Action上下文、值栈等对象,具有更强的数据操作能力主要处理请求和响应对象,不能直接访问Action上下文等
执行顺序在框架内部按照配置的顺序执行在web.xml中或注解中定义的顺序执行

7.应用场景

        拦截器设计模式广泛应用于各种软件架构中,特别是在Web开发、框架设计、消息传递系统等领域。以下是一些典型的应用场景:

        1.Web开发:在Web应用中,拦截器常用于处理HTTP请求和响应,如日志记录、权限验证、请求参数转换等。

        2.框架设计:在软件开发框架中,拦截器模式提供了一种灵活的方式来增强或修改框架的行为,而无需修改框架本身的代码。

        3.消息传递系统:在消息传递系统中,拦截器可用于在消息发送前或接收后进行额外的处理,如消息加密、消息格式转换等。

8.优缺点

优点

  1. 灵活性:拦截器允许在不修改目标对象代码的情况下,通过添加或修改拦截器来增强或修改系统的行为。
  2. 可重用性:拦截器可以被多个目标对象共享和重用,提高了代码的复用率。
  3. 松耦合:拦截器与目标对象之间通过接口或约定进行交互,降低了它们之间的耦合度。

缺点

  1. 复杂性增加:随着拦截器数量的增加,系统的复杂性也会增加,这可能会使得系统难以理解和维护。
  2. 性能影响:在请求处理流程中插入多个拦截器可能会增加系统的处理时间,从而对性能产生一定的影响。

9.总结

        拦截器设计模式是一种强大的设计模式,它允许在请求处理流程中的特定点插入自定义逻辑,以增强或修改原有功能。通过合理使用拦截器,可以在不修改目标对象代码的情况下,灵活地扩展和修改系统的行为。然而,也需要注意拦截器可能带来的复杂性和性能影响问题。

推荐阅读:

过滤器模式

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 前置(3):npm 和npx异同点
  • Layout 布局组件快速搭建
  • package-lock.json 要提交到git吗?
  • string详解(1)
  • 8.7笔记
  • TTL缓存用户数据
  • 基于paddlehub 未戴口罩检测算法
  • Python绘图入门:使用 Matplotlib 绘制折线图
  • MySQL 的 InnoDB 缓冲池里有什么?--InnoDB存储梳理(二)
  • 2024 年最佳 7 款 Java 分析器工具
  • Linux驱动入门实验班——基础驱动模板(附百问网视频链接)
  • MongoDB | MongoDB 终端查询
  • 详细分析Python链接Oracle的多种方式(附Demo)
  • “从头开始训练模型,几乎没有意义”
  • 【亲测有效!】ubuntu20.04和Centos7离线安装docker及nvidia-container-toolkit
  • [PHP内核探索]PHP中的哈希表
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • C++类的相互关联
  • Git 使用集
  • js正则,这点儿就够用了
  • SpringCloud集成分布式事务LCN (一)
  • swift基础之_对象 实例方法 对象方法。
  • Theano - 导数
  • Vue 动态创建 component
  • vue学习系列(二)vue-cli
  • 创建一种深思熟虑的文化
  • 动态规划入门(以爬楼梯为例)
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 新手搭建网站的主要流程
  • ​Benvista PhotoZoom Pro 9.0.4新功能介绍
  • ​Java基础复习笔记 第16章:网络编程
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • ​低代码平台的核心价值与优势
  • ‌前端列表展示1000条大量数据时,后端通常需要进行一定的处理。‌
  • # Swust 12th acm 邀请赛# [ A ] A+B problem [题解]
  • ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTr
  • #QT(智能家居界面-界面切换)
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • $().each和$.each的区别
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (1)STL算法之遍历容器
  • (2024,LoRA,全量微调,低秩,强正则化,缓解遗忘,多样性)LoRA 学习更少,遗忘更少
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (solr系列:一)使用tomcat部署solr服务
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • (十) 初识 Docker file
  • (转)Linux NTP配置详解 (Network Time Protocol)
  • (转)Oracle 9i 数据库设计指引全集(1)
  • ***测试-HTTP方法
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .net遍历html中全部的中文,ASP.NET中遍历页面的所有button控件
  • @31省区市高考时间表来了,祝考试成功
  • @RequestMapping用法详解