一篇文章说清楚Filter(过滤器)、Interceptor(拦截器)和AOP(切面儿)

news2024/7/8 2:51:21

文章目录

    • 前言
    • 一、Filter(过滤器)
      • 1.说明
      • 2.实现
        • filterChain.doFilter()
      • 3.order优先级
      • 4.解决跨域
      • 5.拦截返回错误信息JSON
    • 二、Interceptor(拦截器)
      • 1.说明
      • 2.实现
        • preHandle
        • postHandle
        • afterCompletion
      • 3.执行顺序图
      • 4.排除特定路径拦截 or 加入指定路径拦截
      • 5.拦截返回错误信息JSON
    • 三、AOP(切面儿)
      • 1.说明
      • 2.实现
        • Pointcut切面节点定义
        • doBefore
        • doAfter
        • doAfterReturning
        • doAfterThrowing
        • doAround
      • 3.基于注解在特定方法上实现
      • 4.拦截返回错误信息JSON
    • 四、三者共同点及不同点
      • 1.共同点
      • 2.不同点
    • 五、三者横切节点分析

前言

        Filter、Interceptor、AOP都是用于实现应用横切关注点的技术手段,通过这些技术,可以将横切关注点的代码从核心业务逻辑中解耦,使得代码更加清晰和可维护,同时也提高了代码的复用性。但是三者的应用场景还是有些区别的,Filter主要用于处理HTTP请求和响应,在Servlet容器中工作,可以实现如日志记录、安全性过滤、跨域请求处理等;Interceptor主要用于在Spring MVC中拦截方法调用,允许在方法执行前后添加额外逻辑,用于实现权限拦截、日志记录、事务管理等;AOP用于处理复杂的横切关注点,在不修改核心业务逻辑的情况下增加或调整功能,用于日志记录、事务管理、性能监控等。

一、Filter(过滤器)

1.说明

        Filter主要用于Web应用开发中,基于Servlet规范工作,处理HTTP请求和响应,可以在请求到达Servlet之前进行预处理,也可以在响应返回给客户端之前进行后处理。Filter可以组成过滤链,按照配置的顺序依次处理请求,每个Filter可以在请求进入Servlet之前进行拦截,也可以在响应返回之前对响应进行处理。需要实现Filter接口,重写init()、doFilter()和destroy()方法,其中init()和destroy()分别是初始化和销毁方法,重点是doFilter()方法,实现对请求的处理和转发或对响应的处理。

2.实现

/**
 * 过滤器
 */
@Component
public class WsFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String url = request.getRequestURI(); // 相对方法名路径
        System.out.println("\n【过滤器】请求到达:" + url);
        // 如果没有filterChain.doFilter方法,请求的方法就直接被过滤了,执行不到下面的Controller方法中
        filterChain.doFilter(request, response);
        System.out.println("【过滤器】请求结束:" + url);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
filterChain.doFilter()

项目启用了过滤器,doFilter方法下如果没有filterChain.doFilter(request, response)方法,则程序请求的方法就直接被过滤了,执行不到下面的Controller方法中。

3.order优先级

配置多个Filter,根据不同的order来决定过滤执行的顺序,order越小优先级越高,越大优先级越低。

4.解决跨域

与前端项目交互时,尤其是前后端分离项目,经常会出现跨域问题,这个时候需要配置跨域参数,设置Access-Control允许各类请求通过,解决跨平台交互的跨域问题。如下:

	@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String url = request.getRequestURI(); // 相对方法名路径
        System.out.println("\n【过滤器】请求到达:" + url);

        // 跨域配置
        if (request.getHeader("Origin") != null) {
            response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        }
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me");

        // 如果没有filterChain.doFilter方法,请求的方法就直接被过滤了,执行不到下面的Controller方法中
        filterChain.doFilter(request, response);
        System.out.println("【过滤器】请求结束:" + url);
    }

5.拦截返回错误信息JSON

如果是void无返回值的方法,直接拦截是没有问题的,直接不执行filterChain.doFilter(request, response)方法即可。但是如果是有返回值的,需要在过滤器上构造JSON进行返回,如下:

	@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String url = request.getRequestURI(); // 相对方法名路径
        System.out.println("\n【过滤器】请求到达:" + url);

        returnJson(response, 401, "Filter拦截了");

        // 如果没有filterChain.doFilter方法,请求的方法就直接被过滤了,执行不到下面的Controller方法中
        // filterChain.doFilter(request, response);
        // System.out.println("【过滤器】请求结束:" + url);
    }

    private void returnJson(HttpServletResponse response, int code, String msg) throws IOException {
        // 设置响应的内容类型为JSON
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        // 创建JSON字符串
        Map<String, Object> map = new HashMap<>();
        map.put("code", code);
        map.put("msg", msg);
        String json = JSON.toJSONString(map);
        // 获取PrintWriter来写入响应
        PrintWriter out = response.getWriter();
        out.print(json);
        out.flush();
    }

二、Interceptor(拦截器)

1.说明

        Interceptor主要用于Spring MVC框架中拦截方法的调用,用于框架中方法调用的拦截和处理,拦截器与过滤器(Filter)类似,但更加灵活,通常用于框架级别的功能扩展和定制化。通常用于框架级别的功能扩展,如事务管理、日志记录、权限检查等,使得应用程序可以更加模块化、可维护和可扩展。

2.实现

/**
 * 拦截器
 */
@Component
public class WsInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String url = request.getRequestURI(); // 相对方法名路径
        System.out.println("【拦截器】到达:"+ url);
        return true; // 放开,继续执行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 当前请求进行处理之后,也就是Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用
        System.out.println("【拦截器】请求处理完成,DispatcherServlet准备进行视图返回渲染");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行
        System.out.println("【拦截器】请求结束,DispatcherServlet渲染了对应的视图");
    }
}
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Bean
    public WsInterceptor getWsInterceptor() {
        return new WsInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getWsInterceptor());
    }
}
preHandle

请求处理前preHandle,是在请求处理器(如Controller方法)执行前被调用的方法,允许开发者在实际请求处理前执行特定的预处理逻辑。必须【return true】时,才能执行到后续的controller方法中,如果【return false】则不往下继续执行。

postHandle

请求处理后postHandle,当前请求进行处理之后,也就是Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用。

afterCompletion

视图渲染后afterCompletion,在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。

3.执行顺序图

在这里插入图片描述

4.排除特定路径拦截 or 加入指定路径拦截

如果什么都不处理,默认拦截所有的请求
registry.addPathPatterns(URL)为加入指定路径拦截,则其他路径都放行

	@Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getWsInterceptor()).addPathPatterns("/test/test02");
    }

registry.excludePathPatterns(URL)为排除特定路径拦截,其他路径都拦截

	@Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getWsInterceptor()).excludePathPatterns("/test/test02");
    }

5.拦截返回错误信息JSON

如果是void无返回值的方法,直接拦截是没有问题的,直接【return false】即可。但是如果是有返回值的,需要在拦截器上构造JSON进行返回,如下:

	@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String url = request.getRequestURI(); // 相对方法名路径
        System.out.println("【拦截器】到达:"+ url);
        returnJson(response, 402, "Interceptor拦截了");
        return false;
        // return true; // 放开,继续执行
    }

    private void returnJson(HttpServletResponse response, int code, String msg) throws IOException {
        // 设置响应的内容类型为JSON
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        // 创建JSON字符串
        Map<String, Object> map = new HashMap<>();
        map.put("code", code);
        map.put("msg", msg);
        String json = JSON.toJSONString(map);
        // 获取PrintWriter来写入响应
        PrintWriter out = response.getWriter();
        out.print(json);
        out.flush();
    }

三、AOP(切面儿)

1.说明

        AOP切面是一种编程范式,用于通过将横切关注点从核心业务逻辑中分离出来,使得这些关注点能够被模块化、重用,并且能够有效地降低代码的重复性。它定义了在何处(Pointcut)以及如何(Advice)应用横切关注点到目标对象的行为,增强系统的模块化程度,提高代码的可维护性和可扩展性。

2.实现

@Aspect
@Component
public class WsAop {
    // 定义切面儿的切入点,参数是定义在哪个包。哪个类、哪个方法切入,关于切入点如何定义(对应路径下的某类、某方法,*代表的是全部)
    @Pointcut("execution(* com.jon.demo.controller.*.*(..))")
    public void wsLog(){}

    @Before("wsLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 在切⼊点开始处切⼊内容
        System.out.println("【AOP】before 在切⼊点开始处切⼊内容");
    }

    @After("wsLog()")
    public void doAfter() {
        // 在切⼊点结尾处切⼊内容
        System.out.println("【AOP】doAfter 在切⼊点结尾处切⼊内容");
    }

    @AfterReturning(pointcut = "wsLog()", returning = "result")
    public void doAfterReturning(Object result) {
        // 在切⼊点return内容之后切⼊内容,可以对返回值做⼀些加⼯处理
        System.out.println("【AOP】doAfterReturning 在切⼊点return内容之后切⼊内容");
    }

    @AfterThrowing(pointcut = "wsLog()", throwing = "exception")
    public void doAfterThrowing(Exception exception) {
        // 处理当切⼊内容部分抛出异常之后的逻辑
        System.out.println("【AOP】doAfterThrowing 处理当切⼊内容部分抛出异常之后的逻辑");
    }

    // 环绕通知wsLog方法,切面方法需要有返回值,来代替被代理方法返回结果
    @Around("wsLog()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        // 方法要有返回,如果有多个参数ProceedingJoinPoint要放在第一个
        System.out.println("【AOP】doAround");
        return point.proceed();
    }
}
Pointcut切面节点定义

定义切面儿的切入点,参数是定义在哪个包。哪个类、哪个方法切入,关于切入点如何定义(对应路径下的某类、某方法,*代表的是全部)

doBefore

在切⼊点开始处切⼊内容

doAfter

在切⼊点结尾处切⼊内容

doAfterReturning

在切⼊点return内容之后切⼊内容,可以对返回值做⼀些加⼯处理。

doAfterThrowing

处理当切⼊内容部分抛出异常之后的逻辑

doAround

环绕通知wsLog方法,切面方法需要有返回值,来代替被代理方法返回结果。方法要有返回,如果有多个参数ProceedingJoinPoint要放在第一个。

3.基于注解在特定方法上实现

        不在Pointcut指定特意的类、方法作为切入点,直接自定义注解,并且指定注解的引用地址,这样在对应的方法中加上注解就会引入切面了,这样更加方便和灵活。具体的使用方式,在之前的文章自定义注解(一)——统一请求拦截中有定义!

4.拦截返回错误信息JSON

如果是void无返回值的方法,直接拦截是没有问题的,直接around方法不执行point.proceed()即可。但是如果是有返回值的,需要在around方法中构造JSON,并且在point.proceed()之前进行返回,如下:

	// 环绕通知wsLog方法,切面方法需要有返回值,来代替被代理方法返回结果
    @Around("wsLog()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        // 方法要有返回,如果有多个参数ProceedingJoinPoint要放在第一个
        System.out.println("【AOP】doAround");
        Map<String, Object> map = new HashMap<>();
        map.put("code", 403);
        map.put("msg", "AOP拦截了");
        return map;
//        return point.proceed();
    }

四、三者共同点及不同点

1.共同点

三者都是处理横切关注点,即那些不能分散在核心业务逻辑中处理的功能或需求,例如日志记录、性能监控、事务管理等。它们都支持在运行时动态地将横切关注点的代码织入到目标对象的执行流程中,而不需要修改目标对象的源代码。

2.不同点

Filter工作在Servlet容器中,可以对请求链进行处理,例如处理编码、安全性、日志等。对比于,Filter更多用于整个请求和响应的处理。

Interceptor主要工作Spring容器中,用于框架中方法调用的拦截和处理,通常用于面向对象的框架中,在方法调用前后可以添加额外逻辑。对比于Filter,Interceptor更加专注于方法调用前后的处理。

AOP用于处理复杂的横切关注点,允许在运行时动态地将横切逻辑织入到应用中,可以跨越多个类和模块,不仅限于单个方法或请求处理。可以在代码中定义关注点(如事务、日志、安全性),并在需要时将其应用到目标对象中。

五、三者横切节点分析

在这里插入图片描述
    三者横切的节点不一样,客户端端发起请求后,请求顺序为:请求入口 -> 过滤器filterChain.doFilter之前 -> 拦截器preHandle方法 -> 切面doAround方法 -> 切面before方法 -> Controller下的方法

    返回顺序为:Controller下的方法执行结束 -> 切面doAfterReturning方法 -> 切面doAfter方法 -> 拦截器postHandle方法 -> 拦截器afterCompletion方法 -> 过滤器filterChain.doFilter之后 -> 请求入口

可看执行结果如下:
在这里插入图片描述

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

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

相关文章

C#/.NET/.NET Core编程技巧练习集

DotNet Exercises介绍 DotNetGuide专栏C#/.NET/.NET Core编程常用语法、算法、技巧、中间件、类库练习集&#xff0c;配套详细的文章教程讲解&#xff0c;助你快速掌握C#/.NET/.NET Core各种编程常用语法、算法、技巧、中间件、类库等等。 GitHub开源地址&#xff1a;https://…

MedCLIP:CLIP + 医学语义匹配策略,解决模型误将不同患者同病症视为不相关

MedCLIP&#xff1a;CLIP 医学语义匹配策略&#xff0c;解决模型误将不同患者同病症视为不相关 提出背景流程图解法拆解子解法1&#xff1a;知识提取子解法2&#xff1a;视觉和文本编码器子解法3&#xff1a;语义匹配损失 提出背景 论文&#xff1a;https://arxiv.org/pdf/22…

【SkiaSharp绘图15】SKPath属性详解:边界、填充、凹凸、类型判断、坐标、路径类型

文章目录 SKPath 构造函数SKPath 属性Bounds 边界(宽边界)TightBounds紧边界FillType填充方式IsConcave 是否凹/ IsConvex 是否凸IsEmpty是否为空IsLine是否为线段IsRect是否为矩形IsOval是否为椭圆或圆IsRoundRect是否为圆角矩形Item[] 获取路径的坐标LastPoint最后点的坐标Po…

JavaScript——while类型

目录 任务描述 相关知识 while类型 编程要求 任务描述 质数的定义如下&#xff1a;大于1的自然数&#xff0c;且除了1和本身外没有别的因数。如2、3、5、7。 本关任务&#xff1a;利用循环结构求质数的和。 相关知识 在选择结构中&#xff0c;条件会被测试一次&#xff…

第一百四十三节 Java数据类型教程 - Java Boolean包装类

Java数据类型教程 - Java Boolean包装类 布尔类的对象包装一个布尔值。 Boolean.TRUE和Boolean.FALSE是布尔类型的两个常量&#xff0c;用于表示布尔值true和false值。 我们可以使用构造函数或valueOf()工厂方法创建一个布尔对象。 当解析字符串时&#xff0c;此类将处理“t…

复现centernet时,报错RuntimeError: CUDA error: out of memory

运行 python test.py ctdet --dataset coco --exp_id coco_dla --load_model /root/CenterNet/exp/ctdet/coco_dla/model_last.pth --gpus 0 --test_scales 1 报错下面&#xff1a; RuntimeError: CUDA error: out of memory明明显存是够用的 解决办法&#xff1a; 找到自己…

RK3568平台(opencv篇)ubuntu18.04上安装opencv环境

一.什么是 OpenCV-Python OpenCV-Python 是一个 Python 绑定库&#xff0c;旨在解决计算机视觉问题。   Python 是一种由 Guido van Rossum 开发的通用编程语言&#xff0c;它很快就变得非常流行&#xff0c;主要是 因为它的简单性和代码可读性。它使程序员能够用更少的代码行…

LVS-DR负载均衡

LVS-DR负载均衡 LVS—DR工作模式 原理 客户端访问调度器的VIP地址&#xff0c;在路由器上应该设置VIP跟调度器的一对一的映射关系&#xff0c;调度器根据调度算法将该请求“调度“到后端真实服务器&#xff0c;真实服务器处理完毕后直接将处理后的应答报文发送给路由器&#xf…

[Redis]哨兵机制

哨兵机制概念 在传统主从复制机制中&#xff0c;会存在一些问题&#xff1a; 1. 主节点发生故障时&#xff0c;进行主备切换的过程是复杂的&#xff0c;需要人工参与&#xff0c;导致故障恢复时间无法保障。 2. 主节点可以将读压力分散出去&#xff0c;但写压力/存储压力是无法…

二、基础—常用数据结构:列表、元祖、集合、字典、函数等(爬虫及数据可视化)

二、基础—常用数据结构&#xff1a;列表、元祖、集合、字典、函数等&#xff08;爬虫及数据可视化&#xff09; 1&#xff0c;字符串2&#xff0c;最常用的是列表&#xff08;重点掌握&#xff09;3&#xff0c;元组4&#xff0c;字典&#xff08;重要&#xff09;5&#xff0…

卫星IoT产品发展前景

卫星IoT产品发展前景 一、概述 卫星IoT产品是指利用卫星通信技术实现物联网设备互联互通的解决方案。随着卫星互联网技术的快速发展&#xff0c;卫星IoT产品正逐渐成为解决偏远地区、海洋、航空等场景下物联网连接问题的重要手段。 二、性能特点 广泛覆盖&#xff1a; 卫星…

搜维尔科技:如何使用 SenseGlove Nova 加速手部运动功能的恢复

District XR 的VR 培训 5 年多来&#xff0c;District XR 一直在为最大的工业公司创建 VR 和 AR 项目。 客户&#xff1a;District XR 客户代表&#xff1a;尼古拉沃尔科夫 他的角色&#xff1a;District XR 首席执行官 面临解决的挑战 该公司正在寻找一种方法来加速身体伤…

k8s离线安装安装skywalking9.4

目录 概述资源下载Skywalking功能介绍成果速览实践rbacoapoap-svcuiui-svc 结束 概述 k8s 离线安装安装 skywalking9.4 版本&#xff0c;环境&#xff1a;k8s版本为&#xff1a;1.27.x 、spring boot 2.7.x spring cloud &#xff1a;2021.0.5 、spring.cloud.alibab&#xff1…

搜维尔科技:【研究】Scalefit人体工程学测量系统为预防肌肉骨骼疾病提供生物力学分析

与工作相关的肌肉骨骼疾病(MSE)是工作生活中的一个持续的伴侣。总部位于科隆的Scaleit公司生产的移动生物力学测量系统Industrial Athlete有助于在工作场所立即发现疾病&#xff0c;伤害和损伤的原因。 Scalefit是一个跨学科网络的一部分&#xff0c;在德国科隆体育大学和职业…

Linux手动安装JDK1.8

1、下载要安装的jdk安装包文件 官网下载地址&#xff1a;https://www.oracle.com/cn/java/technologies/downloads/ 2、上传jdk安装包至要安装服务器 3、在要安装jdk位置使用命令解压安装包 安装路径: /usr/local/java 解压安装包&#xff0c;解压命令 tar -zxvf /install…

2024攻防演练:亚信安全新一代WAF,关键时刻守护先锋

实网攻防 网络安全如同一面坚固的盾牌&#xff0c;保护着我们的信息资产免受无孔不入的威胁。而其中&#xff0c;WAF就像网络安全的守门员&#xff0c;关键时刻挺身而出&#xff0c;为您的企业筑起一道坚实的防线。 攻防不对等 防守方实时应答压力山大 在攻防对抗中&#xf…

字符设备驱动程序

简单做个模板框架 字符设备开发流程 确定设备号dev_t&#xff0c;动态分配 alloc_chrdev_region() 或静态分配 register_chrdev_region()定义file_opeartion 结构体*fops *&#xff0c;在结构体成员中实现对应的 *open()、read()*等函数。cdev_init() 将 fops 与 cdev 绑定&…

如何在 Selenium Python 中解决验证码 | 2024 完整指南

由于在进行网络自动化时遇到验证码是让许多人感到不知所措的问题。这些验证码专为区分人类用户和自动化脚本而设计&#xff0c;对于使用Selenium进行网络爬虫或自动化任务而言&#xff0c;无疑是一个巨大的挑战。2024年的完全指南将为您提供全面的解决方案&#xff0c;帮助您高…

Mac本地部署大模型-单机运行

前些天在一台linux服务器&#xff08;8核&#xff0c;32G内存&#xff0c;无显卡&#xff09;使用ollama运行阿里通义千问Qwen1.5和Qwen2.0低参数版本大模型&#xff0c;Qwen2-1.5B可以运行&#xff0c;但是推理速度有些慢。 一直还没有尝试在macbook上运行测试大模型&#xf…