设计模式之拦截器模式

news2024/11/15 8:48:01

目录

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.总结

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

推荐阅读:

过滤器模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1989670.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【自动驾驶】ubuntu20.04安装完整ROS的Noetic版本

目录 安装过程换源及安装注意&#xff1a;三级目录 安装过程 1.配置ubuntu的软件和更新 配置ubuntu的软件和更新&#xff0c;允许安装不经认证的软件。 首先打开“软件和更新”对话框&#xff0c;在设置菜单中的&#xff0c;关于&#xff0c;软件更新。 打开后按照下图进行配…

字符串左旋(c语言)

1.字符串左旋 //实现一个函数&#xff0c;可以左旋字符串的k个字符 例如&#xff1a;ABCD左旋字符串的1个字符BCDA ABCD左旋字符串的2个字符CDAB 2.第一步我们先输入k&#xff08;scanf&#xff09;,将第一位进行储存&#xff0c;然后其他位先前走一位&#xff0c;然后将第一…

分布式事务一站式解决方案-Seata

分布式事务一站式解决方案- 分布式事务一站式解决方案分布式事务产生背景三个概念Seata下载和安装实际业务模拟演示不加 GlobalTransactional 注解&#xff0c;正常操作下单不加 GlobalTransactional 注解&#xff0c;下单过程出异常或者超时了加 GlobalTransactional 注解&…

Shell定时上传日志到HDFS

Shell定时上传日志到HDFS 一、任务需求二、实现思路三、具体实现流程3.1 规划文件上传目录3.2 开发 shell 脚本3.3 授予 shell 可执行权限3.4 手动执行查看3.4 定时执行 shell 脚本 一、任务需求 公司在线服务器每天都会产生网站运行日志&#xff0c;为了避免志文件过大&#…

WPF学习笔记

WPF WPF&#xff08;Windows Presentation Foundation&#xff0c;Windows呈现基础&#xff09;是微软推出的基于Windows 的用户界面框架&#xff0c;属于.NET Framework 3.0的一部分。它提供了统一的编程模型、语言和框架&#xff0c;真正做到了分离界面设计人员与开发人员的…

学习STM32(6)-- STM32单片机ADCDAC的应用

1 引 言 深入了解并掌握STM32F103单片机在模拟数字转换&#xff08;ADC&#xff09;和数字模拟转换&#xff08;DAC&#xff09;应用方面的功能和操作。学习如何配置STM32F103的ADC模块&#xff0c;实现模拟信号到数字信号的精确转换&#xff1b;同时&#xff0c;探索DAC模块…

【AI学习】[2024北京智源大会]具身智能:具身智能关键技术研究:操纵、决策、导航

具身智能关键技术研究&#xff1a;操纵、决策、导航 董 豪 | 北京大学助理教授 依然是边看边做些记录 这张图的重点是在说&#xff0c;我们的大脑&#xff0c;也是不同的部分处理不同的功能。这里面有些功能&#xff0c;比如视觉、听觉理解等功能&#xff0c;LLM已经具备&…

一键浪漫的回忆:微软开源的修复工具!!【送源码】

项目介绍 “Bringing-Old-Photos-Back-to-Life”是一款由微软开发的创新软件解决方案&#xff0c;它利用人工智能技术来修复和增强老旧照片的质量。这款工具可以解决老旧照片中常见的问题&#xff0c;如褪色、低分辨率以及物理损坏&#xff08;如划痕和撕裂&#xff09;。通过采…

分析 Vue3 Effect 函数看响应式依赖收集和触发更新过程

先给一个简单的实例&#xff0c;分析依赖收集和更新的过程 let obj { name: "hefeng6500", age: 10, flag: true }const state reactive(obj);effect(() > {app.innerHTML state.name state.age })setTimeout(() > {state.age 20 }, 1000)reactive 函数只…

吴恩达:如何系统学习机器学习?

最近在知乎圆桌里看到吴恩达的回答&#xff0c;【如何系统学习机器学习&#xff1f;】颇为惊喜&#xff0c;仿佛看到了知乎刚成立时的样子&#xff0c;请各个行业大佬来分享专业知识。 该回答目前已经有三千多赞&#xff0c;评论区也相当火爆&#xff0c;一片膜拜之声。 吴恩…

java学习19VUE

VUE NPM npm的全称是Node Package Manager 中文名为Node.js包管理器&#xff0c;是一个NodeJS包管理和分发工具&#xff0c;已经成为了非官方的发布Node模块(包)的标准。NPM可以方便地从一个全球的代码库中获取并安装Node.js模块&#xff0c;这些模块可以用于构建应用程序、…

开发框架DevExpress XAF v24.2产品路线图预览——增强跨平台性

DevExpress XAF是一款强大的现代应用程序框架&#xff0c;允许同时开发ASP.NET和WinForms。XAF采用模块化设计&#xff0c;开发人员可以选择内建模块&#xff0c;也可以自行创建&#xff0c;从而以更快的速度和比开发人员当前更强有力的方式创建应用程序。 DevExpress XAF是一…

Python 之Scikit-learn(二) -- Scikit-learn标准化数据

在机器学习中&#xff0c;数据标准化是一项关键的预处理步骤。标准化&#xff08;Standardization&#xff09;是将数据转换为具有均值为0和标准差为1的分布。这样可以确保特征在相同的尺度上&#xff0c;有助于提升某些机器学习算法的性能和稳定性。 Scikit-learn提供了一个简…

一篇教会搭建ELK日志分析平台

日志分析的概述 日志分析是运维工程师解决系统故障&#xff0c;发现问题的主要手段日志主要包括系统日志、应用程序日志和安全日志系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因经常分析日志可以了解服务器的负荷&#xff0c;性…

Date类型的字段序列化成JSON字符串

我们发现收到的响应结果里面有一个参数为&#xff1a; 我们收到的时间字符串格式是由JSON序列化框架来决定的。 spring将JAVA数据类型的序列和反序列化为JSON字符串是依赖jackson(com.fasterxml.jackson.core:jackson-core)库来实现的。 Date类型的字段在序列化成JSON字符串时…

【人工智能】人工智能与机器学习的相关介绍

文章目录 人工智能的发展历程人工智能与机器学习关系图谱数据处理机器学习ML和深度学习DL的区别人工智能按照学习方式划分监督学习算法无监督学习算法总结 人工智能的发展历程 重要的时间点了解一下&#xff1a; 早在1950年人工智能就已经开始兴起 1997年deep blue战胜了人类国…

天津美术学院2024级专升本新生开学报道须知

天津美术学院2024级新生入学须知 亲爱的新同学&#xff1a; 祝贺你成为天津美术学院的新成员&#xff0c;开启新的求学历程&#xff01;为方便新生入学&#xff0c;现将有关事宜通知如下&#xff1a; 一、报到安排 1.报到时间&#xff1a;2024年9月6日上午8&#xff1a;30—…

MYSQL的引擎、清空数据的两种方式

目录 1.MYSQL引擎介绍 1.1MySQL的引擎作用&#xff1a; 1.2 MySQL的3类引擎 1.3 MyISAM和InnoDB的区别 1.4设定引擎 2. 清空数据有两种不同的方式 2.1 区别 1.MYSQL引擎介绍 MySQL有多种引擎&#xff0c;能执行create table、select等命令&#xff0c;在数据量不多时…

二十九、MongoDB(1)

&#x1f33b;&#x1f33b; 目录 一、MongoDB简介1.1 什么是 MongoDB1.2 MongoDB特点1.3 MongoDB 体系结构 二、下载与安装2.1 MongoDB下载2.2 在Linux 上的安装2.2.1 安装前的准备2.2.2 开始安装2.2.3 测试软链接启动 2.3 在windows上的安装 三、基本增删改查操作3.1 选择或创…

lvs防火墙mark标记解决调度问题

实验环境是在之前部署DR模式集群的基础上做的&#xff0c;参考如下 部署DR模式集群 以http和https为例&#xff0c;当我们在webserver中同时开放80和443端口&#xff0c;那么默认控制是分开轮询的&#xff0c;就会出现了一个轮询错乱的问题&#xff1a; 当第一次访问80被轮询…