目录
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_ptr
或std::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.优缺点
优点:
- 灵活性:拦截器允许在不修改目标对象代码的情况下,通过添加或修改拦截器来增强或修改系统的行为。
- 可重用性:拦截器可以被多个目标对象共享和重用,提高了代码的复用率。
- 松耦合:拦截器与目标对象之间通过接口或约定进行交互,降低了它们之间的耦合度。
缺点:
- 复杂性增加:随着拦截器数量的增加,系统的复杂性也会增加,这可能会使得系统难以理解和维护。
- 性能影响:在请求处理流程中插入多个拦截器可能会增加系统的处理时间,从而对性能产生一定的影响。
9.总结
拦截器设计模式是一种强大的设计模式,它允许在请求处理流程中的特定点插入自定义逻辑,以增强或修改原有功能。通过合理使用拦截器,可以在不修改目标对象代码的情况下,灵活地扩展和修改系统的行为。然而,也需要注意拦截器可能带来的复杂性和性能影响问题。
推荐阅读:
过滤器模式