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

文章目录

    • 前言
    • 一、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之后 -> 请求入口

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

@react-google-maps/api实现谷歌地图中添加多边围栏,并可编辑,编辑后可获得围栏各个点的经纬度

先上一张效果图 看看是不是大家想要的效果&#xff5e; ❤️ 由于该功能微微复杂一点&#xff0c;为了让大家精准了解 我精简了一下地图代码 大家根据自己的需求将center值和paths&#xff0c;用setState做活就可以了 1.第一步要加入项目package.json中或者直接yarn install它…

在Linux上查找文件的2个好用的命令

1. locate xx &#xff08;查找带xx字符的所有文件或目录&#xff09; 在终端输入命令 locate lua&#xff0c;可以看到&#xff0c;所有带lua字符的文件或目录都会被搜索出来。 2. find / -name xx &#xff08;查找名为xx的文件或目录&#xff09; 在终端输入命令 find …

揭开北斗系统和物联网的神秘面纱:探索未来技术的无限可能性

北斗系统和物联网是现代科技领域的两个重要概念。随着科学技术的快速发展和应用的深化&#xff0c;这两个术语逐渐进入人们的视野。本文将深入探讨北斗系统和物联网的原理、应用和未来发展前景&#xff0c;带您充分了解科技革命的幕后故事。北斗系统&#xff1a;引领全球导航新…

从零开始学习SLAM(三)

旋转向量 #include <Eigen/Geometry> #include <Eigen/Core>AngleAxisd 类有几种构造函数&#xff0c;其中最常用的是&#xff1a; Eigen::AngleAxisd(const Scalar& angle, const Axis& axis);angle 是旋转的角度&#xff0c;通常以弧度表示。axis 是旋…

数据库表导出到excel

数据库表导出到excel:前置知识1 ALL_TAB_COLS 数据库表导出到excel:前置知识2 Quartz基本使用 数据库表导出到excel:前置知识3 项目封装的Quartz实现动态定时任务 数据库表导出到excel:前置知识4 业务和效果 发起清单下载control层InventoryDownloadLogController /* * */ pa…

#### golang中【堆】的使用及底层 ####

声明&#xff0c;本文部分内容摘自&#xff1a; Go: 深入理解堆实现及应用-腾讯云开发者社区-腾讯云 数组实现堆 | WXue 堆&#xff08;Heap&#xff09;是实现优先队列的数据结构&#xff0c;Go提供了接口和方法来操作堆。 应用 package mainimport ("container/heap&q…

LVS-DR群集

LVS-DR集群 LVS-DR(Linux Virtual Server DIrector Server)工作模式&#xff0c;是生产环境中最常用的一种工作模式。 LVS-DR工作原理 LVS-DR模式&#xff0c;Director Server作为群集的访问入口&#xff0c;不作为网关使用&#xff0c;节点DirectorServer与Real Server需要…

光速入门 Tailwind CSS

文章目录 入门安装IDE 设置使用预编译器生产环境优化 基础概念分层指令tailwindlayerapplyconfig 函数theme()screen() 基础案例怎么设置属性任意值&#xff1f;hover 父元素时&#xff0c;怎么选中子元素添加样式&#xff1f;添加 animation 动画 配置主题 Tailwind CSS 中文网…

性能测试-JMeter学习

1、给不同的访问口分配访问占比&#xff1b;例&#xff1a;登录30%&#xff0c;首页&#xff1a;20%&#xff0c;新增&#xff1a;50% 不同业务放到不同线程组里&#xff0c;实现不同业务的分配 使用吞吐量控制器&#xff0c;设置不同的占比 使用if控制器&#xff0c;设置不同…

HX4004A-MFC 低噪声、稳压电荷泵DC/DC转换器芯片IC

一般描述 该HX4004A是一个低噪声开关电容电压倍。它产生一个调节输出电压从2.7V到4.5V的输入。低的外部零件数量(VIN和VOUT处一个飞行电容和两个小型旁路电容)使HX4004A非常适合小型电池供电应用。 该HX4004A具有热关断能力&#xff0c;可以生存从VOUT到GND的连续…

【pytorch13】激活函数及梯度

什么是激活函数 计算机科学家借鉴生物的神经元机制发明了计算机上的模型&#xff0c;这个模型与生物的神经元非常类似 激活的意思就是z变量要大于0&#xff0c;这一个节点才会激活&#xff0c;否则就会处于睡眠状态不会输出电平值 该激活函数在z0处不可导&#xff0c;因此不能…

地级市空气质量指数AQI、环境污染PM2.5、SO2

2015-2021年地级市月度空气质量数据&#xff08;AQI、SO2、NO2、PM2.5、PM10、O3、CO&#xff09; 目录 探究环境污染对经济增长的影响 一、引言 二、数据来源与描述性统计 三、实证模型 &#xff08;一&#xff09;模型设定 &#xff08;二&#xff09;变量说明 四、程…

混元大模型加持,微信输入法开启AI问答新体验

在人工智能技术飞速发展的今天&#xff0c;微信作为全球最大的社交平台之一&#xff0c;一直在不断地探索和创新&#xff0c;以提供更智能、更便捷的用户体验。 最近&#xff0c;微信官方宣布了一个令人兴奋的消息&#xff1a;微信输入法正式上线了“一键AI问答”功能&#xf…

【Python机器学习】算法链与管道——通用的管道接口

Pipeline类补单可以用于预处理和分类&#xff0c;实际上还可以将任意数量的估计器连接在一起。例如&#xff0c;我们可以构建一个包含特征提取、特征选择、缩放和分类的管道&#xff0c;总共有4个步骤。同样的&#xff0c;最后一步可以用聚类或回归代替。 对于管道中估计器的唯…

【机器学习】Datawhale-AI夏令营分子性质AI预测挑战赛

参赛链接&#xff1a;零基础入门 Ai 数据挖掘竞赛-速通 Baseline - 飞桨AI Studio星河社区 一、赛事背景 在当今科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;技术正以前所未有的深度和广度渗透到科研领域&#xff0c;特别是在化学及药物研发中展现出了巨…

警翼警用记录仪视频格式化后恢复方法

警翼是国内较大的一家警用记录仪厂商&#xff0c;此品牌我们恢复过很多&#xff0c;此次遇到的是一个典型的误格式化的情况&#xff0c;我们来看看误格式化后如何恢复。 故障存储: 32G卡/fat32 故障现象: 客户提供的信息是在交接设备后没有及时备份而做出了初始化设备的操…

fluwx插件实现微信支付

Flutter开发使用fluwx插件实现微信支付&#xff0c;代码量不多&#xff0c;复杂的是安卓和iOS的各种配置。 在 pubspec.yaml 文件中添加fluwx依赖 fluwx: ^4.5.5 使用方法 通过fluwx注册微信Api await Fluwx().registerApi(appId: wxea7a1c53d9e5849d, universalLink: htt…

机器人控制系列教程之Delta机器人动力学分析

动力学简介 机器人动力学分析是已知各运动构件的尺寸参数和惯性参数的情况下,求解末端运动状态与主驱动力矩之间的函数关系。 意义:对并联机器人动力学分析的意义体现在: 为伺服电机的选型提供理论依据;获得动力学参数为目标函数的最优问题做性能评价指标;为高精度控制提…

内容为王:揭秘顶尖品牌的内容营销制胜法宝

内容营销是当今互联网市场推广领域的热门话题&#xff0c;因为它可以帮助企业更好地与受众沟通、建立品牌口碑&#xff0c;增加销售量。 根据咱们何策网的资源库里的SocialBeta2024年最新《2024 内容营销 10 大趋势》的报告来看&#xff0c;品牌在未来内容营销中最应该注重的是…

2024亚太杯中文赛数学建模B题【洪水灾害的数据分析与预测】思路详解

2024 年第十四届 APMCM 亚太地区大学生数学建模竞赛 B题 洪水灾害的数据分析与预测 附件 train.csv 中提供了超过 100 万的洪水数据&#xff0c;其中包含洪水事件的 id、季风强度、地形排水、河流管理、森林砍伐、城市化、气候变化、大坝质量、淤积、农业实践、侵蚀、无效防灾、…