• spring boot 过滤器&拦截器与aop


    一、应用场景

    1.1 过滤器(Filter)

    在使用 Spring 框架时,可以通过在 web.xml 配置文件中注册过滤器,使其在请求进入 Spring 前就能够进行预处理。这样可以在请求进入 Spring MVC 的 DispatcherServlet 之前,对请求进行拦截、修改或者过滤。

    过滤器在 Spring 中的应用场景包括但不限于:

    1. 字符编码过滤:通过过滤器,在进入 Spring MVC 之前对请求进行字符编码的设置,确保数据的正确传输和处理。

    2. 认证与授权过滤:通过过滤器对请求进行认证和授权的检查,例如验证用户的身份信息,判断用户是否具有访问权限等。

    3. 日志记录过滤:通过过滤器在请求进入 Spring MVC 之前,记录请求的相关信息,以便后续的日志分析和排查问题。

    需要注意的是,过滤器是与具体的框架无关的,因此在使用过滤器时,Spring 不会对其进行特殊的处理。过滤器只是在请求的生命周期中工作,并通过链式调用来实现多个过滤器的组合。

    总结起来,虽然过滤器在 Spring 中使用的是 Servlet 规范,但是在整个请求处理流程中,过滤器工作在 Spring 的上一层,可以对请求进行预处理,为后续的 Spring MVC 处理提供更灵活的控制能力。

    1.2 拦截器(Interceptor)

    拦截器(Interceptor)是 Spring MVC 框架提供的一种机制,用于对请求进行拦截和处理。拦截器在 Spring MVC 的处理流程中工作,在控制器(Controller)方法执行前后进行预处理和后处理操作,因此可以应用于以下场景:

    1. 认证与授权:拦截器可以用于实现认证和授权的逻辑,例如检查用户是否登录、验证用户权限等。在进入具体的控制器方法之前,通过拦截器可以拦截未经认证或未授权的请求。

    2. 日志记录:拦截器可以用于记录请求的相关日志信息,如请求的 URL、请求参数、处理时间等。通过拦截器,在请求进入控制器方法之前或之后,可以将这些信息进行记录,方便后续的日志分析和监控。

    3. 异常处理:拦截器可以捕获控制器方法中抛出的异常,并进行相应的处理,如统一的异常封装、返回错误信息等。通过拦截器,可以实现对异常的集中处理,避免在每个控制器方法中都进行异常处理的重复代码。

    4. 参数预处理:拦截器可以对请求的参数进行预处理,如参数的验证、格式转换等。通过拦截器,可以在进入控制器方法之前对请求参数进行统一的处理,减少控制器方法中的参数处理逻辑。

    5. 缓存控制:拦截器可以用于对响应进行缓存的控制,例如在满足一定条件时,直接返回缓存的响应,减少服务器的压力。通过拦截器,可以实现自定义的缓存策略,灵活地控制缓存的生效条件。

    需要注意的是,拦截器是 Spring MVC 框架特有的概念,并且只对控制器层起作用,不会影响到其他层的业务逻辑。拦截器与过滤器(Filter)相比,更加专注于请求处理和控制器层面的操作。

    总结起来,拦截器适用于对请求进行拦截、预处理和后处理的场景,包括认证与授权、日志记录、异常处理、参数预处理和缓存控制等。通过拦截器,可以实现灵活的请求处理逻辑,提高代码的可维护性和可重用性。

    实际开发中确实更常见使用过滤器(Filter),而拦截器(Interceptor)使用的场景相对较少。这主要是因为过滤器和拦截器在功能上存在一些区别,适用于不同的需求。

    过滤器(Filter)是基于 Servlet 规范的一种机制,可以对请求进行过滤、修改和拦截处理。它工作在请求进入框架之前,可以对所有请求进行统一处理,例如字符编码设置、认证与授权、日志记录等。过滤器是在整个请求的生命周期中起作用,可以对请求进行全局性的预处理和后处理。由于过滤器是在底层框架之前调用的,它具有更高的优先级,可以在请求到达框架之前拦截请求。

    拦截器(Interceptor)是 Spring MVC 框架内部提供的一种机制。它工作在控制器方法执行前后,对请求进行拦截和处理。拦截器的主要作用是在进入控制器方法之前和之后进行预处理和后处理操作,如认证与授权、日志记录、异常处理等。拦截器更专注于控制器层面的操作,可以对请求进行细粒度的拦截和处理。

    虽然拦截器使用的场景相对较少,但在某些需要对请求进行精细控制的情况下,拦截器可以提供更灵活和可扩展的功能。比如针对某一类请求需要特殊处理、需要记录请求处理时间等,这样的场景更适合使用拦截器来实现。

    综上所述,过滤器和拦截器各有其特点,在实际开发中根据具体需求选择使用哪种机制,或者结合使用两者来满足不同的业务需求。过滤器更加通用且功能强大,而拦截器更加专注于控制器层面的处理。

    二、过滤器 (Filter

    2.1 什么是过滤器

     过滤器Filter基于Servlet实现,过滤器在spring上一层,所以不能通过@Component注解交给spring 管理。但是拦截器可以通过@Component交给spring管理。

    过滤器的主要应用场景是对字符编码、跨域等问题进行过滤。

    Servlet的工作原理是拦截配置好的客户端请求,然后对Request和Response进行处理。

    Filter过滤器随着web应用的启动而启动,只初始化一次。

    在开发过程中,拦截器用的很少,大部分使用场景都是过滤器。

    2.2 springboot配置过滤器

    在 Spring Boot 应用中,应用过滤器的几种常见的方式:

    1. 注解方式:通过 @WebFilter 注解将过滤器类标记为一个过滤器,并通过 @ServletComponentScan 注解或在配置类上添加 @Bean 注解来启用过滤器。

    2. 配置类方式:创建一个配置类,并在其中通过 FilterRegistrationBean 对象注册过滤器。可以使用 @Configuration 注解来声明配置类。

    3. Servlet 3.0+ 规范的方式:在过滤器类上直接使用 @WebFilter 注解,并在启动类上添加 @ServletComponentScan 注解或通过 FilterRegistrationBean 进行注册。

    4. 自定义 FilterRegistrationBean 方式:创建一个 FilterRegistrationBean 实例,并设置过滤器类、URL 模式、名称和顺序等属性。

    无论使用哪种方式,过滤器的基本功能和操作是相同的。选择合适的方式取决于你的项目需求和个人偏好。

    2.3 方式一:@WebFilter注解标记过滤器+配置类注册过滤器

    2.3.1 简单步骤:

    1、创建过滤器,实现接口Filter

    2、使用注解@WebFilter来标记过滤器

    3、配置类,将过滤器注册到Spring容器中

    在项目中注册过滤器。通过@Configuration注解将BaseConfiguration类标记为配置类,然后通过@Bean注解将MyFilter过滤器注册到Spring容器中。

    2.3.2  详细步骤:

    1、创建过滤器,实现接口Filter

    在Controller包中,创建MyFilter过滤器,实现Filter接口

    1. package com.example.com_chenshuai.Controller;
    2. import lombok.extern.slf4j.Slf4j;
    3. import javax.servlet.*;
    4. import javax.servlet.annotation.WebFilter;
    5. import javax.servlet.http.HttpServletRequest;
    6. import java.io.IOException;
    7. // 过滤器
    8. @Slf4j
    9. @WebFilter(filterName = "myFilter",urlPatterns = "/*")
    10. public class MyFilter implements Filter {
    11. @Override
    12. public void init(FilterConfig filterConfig) throws ServletException {
    13. // 初始化时调用
    14. Filter.super.init(filterConfig);
    15. }
    16. @Override
    17. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    18. log.info("filter begin ...");
    19. // 强转
    20. HttpServletRequest request = (HttpServletRequest) servletRequest;
    21. // 获取请求url
    22. String requestURI = request.getRequestURI();
    23. // 获取请求 参数
    24. String requestParameter = request.getParameter("");
    25. // FilterChain类似于责任链,
    26. // 继续调用下一个过滤器,如果下一个没有过滤器,则继续往下走(比如调用业务代码之类的)。
    27. filterChain.doFilter(servletRequest,servletResponse);
    28. // 如果基于url进行拦截,可以使用过滤器
    29. // servletResponse 是返回结果,过滤器可以改写返回结果。
    30. // 拦截器,我们也可以获取请求的url、参数等。也可以获取返回结果。但是我们不会在拦截器里改写返回结果。
    31. // 在开发过程中,拦截器用的很少,大部分使用场景都是过滤器。
    32. // 过滤器在spring上一层,所以不能通过spring注入过滤器的实例。但是拦截器可以通过spring注入。
    33. // aop不能获取请求的参数等信息,和返回结果。aop能获取到调用的类,方法及方法参数。以及方法的返回值。
    34. // 如果想通过方法判断,需要使用aop。如果想通过请求判断,需要使用过滤器。
    35. }
    36. @Override
    37. public void destroy() {
    38. // 销毁时调用
    39. Filter.super.destroy();
    40. }
    41. }

    注解@WebFilter(filterName = "myFilter",urlPatterns = "/*")

    filterName = "xxFilter",设置过滤器名称

    urlPatterns = "/*",设置过滤的url,这里/*表示过滤所有的url。

    方法介绍:

     init() :web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。(这个我们不需要太关注)

    destroy() 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次(这个我们不需要太关注)

    doFilter() :该方法完成实际的过滤操作,当客户端请求方法与过滤器设置匹配的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain用户访问后续过滤器。

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 

    我们可以通过ServletRequest servletRequest 获取请求的相关信息,比如url、参数等。(这里需要先强转成HttpServletRequest)

    1. // 强转
    2. HttpServletRequest request = (HttpServletRequest) servletRequest;
    3. // 获取请求url
    4. String requestURI = request.getRequestURI();
    5. // 获取请求 参数
    6. String requestParameter = request.getParameter("");

     我们可以通过ServletResponse servletResponse 获得请求的返回结果。我们可以通过过滤器来改写返回结果。

    FilterChain filterChain 类似于责任链。

    1. // 继续调用下一个过滤器,如果下一个没有过滤器,则继续往下走(比如调用业务代码之类的)
    2. filterChain.doFilter(servletRequest,servletResponse);

    责任链相关资料:

    责任链设计模式_做测试的喵酱的博客-CSDN博客

    这个过滤器的功能是在请求被处理之前和响应被发送回客户端之前,对请求和响应进行处理。该过滤器使用了Slf4j日志框架来输出日志,可以记录请求信息和过滤器执行情况。具体功能包括:

    1. 获取请求URL和参数,并输出日志。
    2. 调用FilterChain继续执行其他过滤器或业务代码。
    3. 在请求处理完毕后可以修改响应结果,但该过滤器并没有实现此功能。
    4. 在过滤器的init()和destroy()方法中可以进行初始化和销毁操作。

    通常情况下,过滤器可以用于请求的预处理、请求的后处理和数据的加密解密等,例如:检查用户是否登录、编码解码、防止跨站点脚本攻击、防御拦截非法URL等等。

     

    2、将过滤器交给spring管理

    在@Configuration类手动配置过滤器,@Bean标识FilterRegistrationBean

    一、Spring管理类&注入实例_做测试的喵酱的博客-CSDN博客

    1. package com.example.com_chenshuai.configuration;
    2. import com.example.com_chenshuai.Controller.LoginInterceptor;
    3. import com.example.com_chenshuai.Controller.MyFilter;
    4. import org.springframework.boot.web.servlet.FilterRegistrationBean;
    5. import org.springframework.boot.web.servlet.ServletComponentScan;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.context.annotation.Configuration;
    8. import org.springframework.context.annotation.EnableAspectJAutoProxy;
    9. import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
    10. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    11. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    12. @Configuration
    13. public class BaseConfiguration {
    14. // 将这个类交给spring管理。不使用注解的方式,因为使用注解,需要在源码上@Component
    15. // 使用方法,返回@Bean
    16. // FilterRegistrationBean 这个类,是第三方的,我们不能加@Component
    17. @Bean
    18. public FilterRegistrationBean getFilter(){
    19. FilterRegistrationBean filter = new FilterRegistrationBean(new MyFilter());
    20. return filter;
    21. }
    22. }

    我们将过滤器交给spring管理,就可以了。不需要关注工程是如何调用过滤器的。(应该是通过监听方式调用的 )

     
    

    三、拦截器(Interceptor

    3.1 什么是拦截器

     拦截器(Interceptor)同 Filter 过滤器一样,它俩都是面向切面编程——AOP 的具体实现。你可以使用 Interceptor 来执行某些任务,例如在 Controller 处理请求之前编写日志,添加或更新配置……,在 Spring中,当请求发送到 Controller 时,在被Controller处理之前,它必须经过 Interceptors(0或多个)。

    3.2 使用拦截器方法

    1、创建拦截器,实现接口HandlerInterceptor。并通过注解@Component交给Spring管理

    1. package com.example.com_chenshuai.Controller;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.springframework.stereotype.Component;
    4. import org.springframework.web.servlet.HandlerInterceptor;
    5. import org.springframework.web.servlet.ModelAndView;
    6. import javax.servlet.http.HttpServletRequest;
    7. import javax.servlet.http.HttpServletResponse;
    8. // 拦截器
    9. @Slf4j
    10. @Component
    11. public class LoginInterceptor implements HandlerInterceptor {
    12. @Override
    13. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    14. // 拦截器,我们也可以获取请求的url、参数等。也可以获取返回结果。
    15. log.info("interceptor pre begin..");
    16. String token = request.getHeader("token");
    17. if(token !=null&&token.equals("1234")){
    18. log.info("login success");
    19. // return true 继续往下走,调用下一个拦截器
    20. return true;
    21. }
    22. // return false 就拦截该请求
    23. return false;
    24. }
    25. @Override
    26. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    27. log.info("interceptor post begin..");
    28. }
    29. @Override
    30. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    31. log.info("interceptor afterCompletion");
    32. }
    33. }

    方法解释:

    preHandler(HttpServletRequest request, HttpServletResponse response, Object handler)

    方法在请求处理之前被调用。该方法在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回至是 Boolean 类型,当它返回 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当它返回为 true 时会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法。 

    我们可以通过request,获取用户请求的相关信息,如url、参数、header等等。

    String token = request.getHeader("token");

    我们可以通过response获取,请求的返回结果。做相应的处理

    postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 

    方法在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。

    afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 

    方法需要在当前对应的 Interceptor 类的 preHandle 方法返回值为 true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。此方法主要用来进行资源清理。

    2、在配置类@Configuration中配置

    配置类需要实现接口 WebMvcConfigurer,然后重写addInterceptors

    1. package com.example.com_chenshuai.configuration;
    2. import com.example.com_chenshuai.Controller.LoginInterceptor;
    3. import com.example.com_chenshuai.Controller.MyFilter;
    4. import org.springframework.boot.web.servlet.FilterRegistrationBean;
    5. import org.springframework.boot.web.servlet.ServletComponentScan;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.context.annotation.Configuration;
    8. import org.springframework.context.annotation.EnableAspectJAutoProxy;
    9. import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
    10. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    11. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    12. @Configuration
    13. public class BaseConfiguration implements WebMvcConfigurer {
    14. // 将这个类交给spring管理。不使用注解的方式,因为使用注解,需要在源码上@Component
    15. // 使用方法,返回@Bean
    16. // FilterRegistrationBean 这个类,是第三方的,我们不能加@Component
    17. @Bean
    18. public FilterRegistrationBean getFilter(){
    19. FilterRegistrationBean filter = new FilterRegistrationBean(new MyFilter());
    20. return filter;
    21. }
    22. @Override
    23. public void addInterceptors(InterceptorRegistry registry) {
    24. InterceptorRegistration interceptorRegistration = registry.addInterceptor(new LoginInterceptor());
    25. // 哪些请求被拦截
    26. // 注意,这里是两个*
    27. interceptorRegistration.addPathPatterns("/**");
    28. // 哪些请求不被拦截
    29. // interceptorRegistration.excludePathPatterns();
    30. }
    31. }

    在 addInterceptors方法中,addPathPatterns表示对哪些请求进行拦截。/**这里表示所有的请求。注意是2个*

    1. // 哪些请求被拦截
    2. // 注意,这里是两个*
    3. interceptorRegistration.addPathPatterns("/**");

    excludePathPatterns表示哪些请求 ,不被拦截

    1. // 哪些请求不被拦截
    2. // interceptorRegistration.excludePathPatterns("");

    四、拦截器&过滤器与spring aop的区别

    过滤器是基于方法回调实现的。拦截器是基于反射实现的。

    4.1 区别

    作用域大小不一样。

    过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。

    拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。

    过滤器/拦截器与aop获取的信息不一样。过滤器/拦截器获取的是请求级别的信息,如url、传参、header、返回值等信息。

    aop不能获取请求的参数等信息,和返回结果。aop能获取到调用的类,方法及方法参数。以及方法的返回值。

    如果想通过方法进行拦截/过滤等处理,需要使用aop。如果想通过请求判断,需要使用过滤器。但是aop获取的是方法级别的信息,类、方法、方法的传参等等。

    4.2添加aop

    Spring AOP面向切面编程_做测试的喵酱的博客-CSDN博客

    编写aop类

    1. package com.example.com_chenshuai.Controller;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.aspectj.lang.JoinPoint;
    4. import org.aspectj.lang.annotation.After;
    5. import org.aspectj.lang.annotation.Aspect;
    6. import org.aspectj.lang.annotation.Before;
    7. import org.aspectj.lang.annotation.Pointcut;
    8. import org.springframework.stereotype.Component;
    9. @Aspect
    10. @Slf4j
    11. @Component
    12. public class MyAspect {
    13. // 设置切点
    14. @Pointcut("execution(public * com.example.com_chenshuai.Controller.*.*(..) )")
    15. public void my(){
    16. }
    17. //
    18. @Before("my()")
    19. public void before(JoinPoint joinPoint){
    20. // 获得方法的参数
    21. Object[] args = joinPoint.getArgs();
    22. log.info("aspect before....");
    23. }
    24. @After("my()")
    25. public void after(){
    26. log.info("aspect after....");
    27. }
    28. }

    在@Configuration配置类中,添加注解@EnableAspectJAutoProxy就可以了。

    1. package com.example.com_chenshuai.configuration;
    2. import com.example.com_chenshuai.Controller.LoginInterceptor;
    3. import com.example.com_chenshuai.Controller.MyFilter;
    4. import org.springframework.boot.web.servlet.FilterRegistrationBean;
    5. import org.springframework.boot.web.servlet.ServletComponentScan;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.context.annotation.Configuration;
    8. import org.springframework.context.annotation.EnableAspectJAutoProxy;
    9. import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
    10. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    11. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    12. @Configuration
    13. @EnableAspectJAutoProxy
    14. public class BaseConfiguration implements WebMvcConfigurer {
    15. // 将这个类交给spring管理。不使用注解的方式,因为使用注解,需要在源码上@Component
    16. // 使用方法,返回@Bean
    17. // FilterRegistrationBean 这个类,是第三方的,我们不能加@Component
    18. @Bean
    19. public FilterRegistrationBean getFilter(){
    20. FilterRegistrationBean filter = new FilterRegistrationBean(new MyFilter());
    21. return filter;
    22. }
    23. @Override
    24. public void addInterceptors(InterceptorRegistry registry) {
    25. InterceptorRegistration interceptorRegistration = registry.addInterceptor(new LoginInterceptor());
    26. // 哪些请求被拦截
    27. // 注意,这里是两个*
    28. interceptorRegistration.addPathPatterns("/**");
    29. // 哪些请求不被拦截
    30. // interceptorRegistration.excludePathPatterns("");
    31. }
    32. }

     参考:

    拦截器和过滤器_学习中的小亮的博客-CSDN博客_拦截器与过滤器

  • 相关阅读:
    一篇文章彻底理解 HDFS 的安全模式
    tcp/ip协议和opc协议对比详解
    有什么低价好用的电容笔推荐?大一新生必备物品
    Vue的生命周期函数
    使用pymodbus进行modbus-TCP通信
    刚入职,软件测试岗,有点迷茫不知道下一步怎么提升自己
    高并发 发送请求(asyncio)
    LabVIEW中图像显示错误
    刷爆 LeetCode 周赛 337,位掩码/回溯/同余/分桶/动态规划·打家劫舍/贪心
    LeetCode——半有序排列
  • 原文地址:https://blog.csdn.net/qq_39208536/article/details/128007786