从使用教程、实现原理、差异对比全方面带你玩转业务系统中高频使用的过滤器与拦截器

1.概述

在Java Web开发中,过滤器(Filter)和拦截器(Interceptor) 是两种常见的组件,用于在请求到达目标资源之前或之后执行一些操作,如日志记录、权限控制、字符编码处理等。虽然它们的作用有些类似,但在实际应用中又有不同之处。本文将深入探讨过滤器与拦截器的实现原理、区别差异以及在实际应用中的使用场景和示例。

过滤器(Filter)

过滤器是一种基于Java Servlet规范的组件,用于在请求到达Servlet之前或响应离开Servlet之后对请求和响应进行预处理和后处理。过滤器通常用于执行以下操作:

  • 请求预处理:对请求进行预处理,如验证用户身份、设置请求编码等。
  • 响应后处理:对响应进行后处理,如添加响应头、设置响应编码等。

过滤器通过实现javax.servlet.Filter接口来定义,并可以在web.xml文件中配置。过滤器的生命周期由Servlet容器管理,可以在应用程序启动时初始化,并在应用程序关闭时销毁。

拦截器(Interceptor)

拦截器是Spring框架提供的一种机制,用于在Spring MVC中拦截请求和响应,并允许开发人员在处理请求之前和之后执行自定义逻辑。拦截器通常用于以下目的:

  • 请求预处理:在控制器处理请求之前执行预处理逻辑,如日志记录、权限验证等。
  • 响应后处理:在控制器处理完请求后执行后处理逻辑,如日志记录、性能监控等。

拦截器通过实现org.springframework.web.servlet.HandlerInterceptor接口来定义,并通过配置文件(如XML配置或Java配置)进行注册。拦截器的生命周期由Spring容器管理,可以在应用程序启动时初始化,并在应用程序关闭时销毁。

2.过滤器Filter

2.1 使用示例

创建 Filter 处理类,并实现javax.servlet.Filter 接口,先来看看Filter定义:

java 复制代码
public interface Filter {
​
    public default void init(FilterConfig filterConfig) throws ServletException {}
​
   
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;
​
    public default void destroy() {}
}

看到Filter 接口中定义了三个方法。

  • init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。「注意」:这个方法必须执行成功,否则过滤器会不起作用。
  • doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter
  • destroy(): 当容器销毁过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次

创建两个过滤器AFilter,BFilter

less 复制代码
@Component
@Slf4j
@WebFilter(urlPatterns = "/*")
@Order(2)
public class AFilter implements Filter {
​
​
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("过滤器A: 执行init方法了");
    }
​
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("过滤器A: 执行doFilter方法了");
        // 放行
        filterChain.doFilter(servletRequest, servletResponse);
    }
​
    @Override
    public void destroy() {
        log.info("过滤器A: 执行destroy方法了");
    }
}
less 复制代码
@Component
@Slf4j
@WebFilter(urlPatterns = "/*")
@Order(1)
public class BFilter implements Filter {
​
​
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("过滤器B: 执行init方法了");
    }
​
​
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("过滤器B: 执行doFilter方法了");
        // 放行
        filterChain.doFilter(servletRequest, servletResponse);
​
    }
​
​
    @Override
    public void destroy() {
        log.info("过滤器B: 执行destroy方法了");
    }
}

添加接口进行测试:

less 复制代码
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
​
    @GetMapping("/filter")
    public void testFilter() {
        log.info("controller业务方法执行了");
    }
​
}

启动项目,会执行过滤器的初始化init()方法:

kotlin 复制代码
INFO com.shepherd.basedemo.filter.BFilter init [main@5838] : 过滤器B: 执行init方法了
INFO com.shepherd.basedemo.filter.AFilter init [main@5838] : 过滤器A: 执行init方法了

同理可得,当关闭服务师会执行destroy()方法,这里就不做结果展示了。

postman访问接口:http://127.0.0.1:8080/test/filter,控制台日志打印如下。

perl 复制代码
INFO com.shepherd.basedemo.filter.BFilter doFilter [http-nio-8080-exec-1@5838] : 过滤器B: 执行doFilter方法了
INFO com.shepherd.basedemo.filter.AFilter doFilter [http-nio-8080-exec-1@5838] : 过滤器A: 执行doFilter方法了
INFO com.shepherd.basedemo.controller.TestController testFilter [http-nio-8080-exec-1@5838] : controller业务方法执行了

上面直接用@WebFilter就可以进行过滤器配置,同时使用@Order控制过滤器执行顺序。如果我们不使用@Order标注,那么默认是按照过滤器bean注入到容器中顺序执行的 。所以默认会按照Spring包扫描Afilter会先被注入到容器,所以就变成了先执行AFilterdoFilter()方法了。除了使用@WebFilter方式配置过滤器,我们还可以通过Spring Boot提供的FilterRegistrationBean注册Filter

java 复制代码
@Configuration
public class MyConfig {
​
    @Resource
    private AFilter aFilter;
    @Resource
    private BFilter bFilter;
​
    @Bean
    public FilterRegistrationBean buildBFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        // 设置过滤器B, 这里使用容器中注入的filter bean,如果自己new一个filter就不能成功依赖注入bean
        filterRegistrationBean.setFilter(bFilter);
        // filterRegistrationBean.setFilter(new BFilter());
​
        // 设置过滤器拦截的url通配符
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setName("BFilter");
        filterRegistrationBean.setOrder(2);
        return filterRegistrationBean;
    }
​
​
    @Bean
    public FilterRegistrationBean buildAFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        // 设置过滤器A
        filterRegistrationBean.setFilter(aFilter);
        // 设置过滤器拦截的url通配符
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setName("AFilter");
        filterRegistrationBean.setOrder(1);
        return filterRegistrationBean;
    }
​
}

注意,FilterRegistrationBean设置过滤器请使用Spring容器中对应的过滤器bean,如果自己new一个filter会导致过滤器不能成功依赖注入bean 。你可能观察到我们这里先注入了BFilter,再注入AFilter,但是设置了order,上面的过滤器类定义代码去掉@WebFilter(urlPatterns = "/*")和@Order(),重启调接口结果如下:

less 复制代码
2024-05-10 17:35:26.278 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.filter.AFilter doFilter [http-nio-8080-exec-1@6811] : 过滤器A: 执行doFilter方法了
2024-05-10 17:35:26.279 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.filter.BFilter doFilter [http-nio-8080-exec-1@6811] : 过滤器B: 执行doFilter方法了
2024-05-10 17:35:26.319 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.controller.TestController testFilter [http-nio-8080-exec-1@6811] : controller业务方法执行了

2.2 实现原理

过滤器是基于方法回调实现的,这也是每个过滤器重写doFilter()方法时都通过filterChain.doFilter(servletRequest, servletResponse)进行放行。debug代码你会发现每次发起请求都会来到Tomcat容器的构造拦截器链的方法ApplicationFilterFactory的#createFilterChain()

scss 复制代码
 public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {
​
        // If there is no servlet to execute, return null
        // servlet为空直接返回
        if (servlet == null) {
            return null;
        }
​
        // Create and initialize a filter chain object
        // 构建一个过滤器链对象
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }
​
        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
​
        // Acquire the filter mappings for this Context
        // 获取过滤器定义映射
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();
​
        // If there are no filter mappings, we are done
        // 映射为空,返回
        if ((filterMaps == null) || (filterMaps.length == 0)) {
            return filterChain;
        }
​
        // Acquire the information we will need to match filter mappings
        // 获取匹配过滤器映射所需的信息
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
​
        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }
​
        String servletName = wrapper.getName();
​
        // Add the relevant path-mapped filters to this filter chain
        // 将对应匹配的路径映射过滤器添加到这个过滤器链中
        for (FilterMap filterMap : filterMaps) {
            if (!matchDispatcher(filterMap, dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMap, requestPath)) {
                continue;
            }
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMap.getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
​
        // Add filters that match on servlet name second
        // 添加与servlet name second匹配的过滤器
        for (FilterMap filterMap : filterMaps) {
            if (!matchDispatcher(filterMap, dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMap, servletName)) {
                continue;
            }
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMap.getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
​
        // Return the completed filter chain
        return filterChain;
    }

这里构造出过滤器链,ApplicationFilterChain里面能拿到过滤器链进行遍历执行每一个过滤器,在其内部回调方法doFilter()里调用各个自定义xxxFilter过滤器,并执行 doFilter() 方法。

vbscript 复制代码
public final class ApplicationFilterChain implements FilterChain {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response) {
            ...//省略
            internalDoFilter(request,response);
    }
 
    private void internalDoFilter(ServletRequest request, ServletResponse response){
    if (pos < n) {
            //获取第pos个filter    
            ApplicationFilterConfig filterConfig = filters[pos++];        
            Filter filter = filterConfig.getFilter();
            ...
            filter.doFilter(request, response, this);
        }
    }
 
}
​

而每个xxxFilter 会先执行自身的 doFilter() 过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest, servletResponse),也就是回调ApplicationFilterChaindoFilter() 方法,以此循环执行实现函数回调。

3.拦截器Interceptor

3.1 使用示例

拦截器是Spring框架提供,创建一个拦截器,只需要实现 HandlerInterceptor 接口即可:

less 复制代码
public interface HandlerInterceptor {
​
  default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
​
    return true;
  }
​
  default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
      @Nullable ModelAndView modelAndView) throws Exception {
  }
​
  default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
      @Nullable Exception ex) throws Exception {
  }
​
}
​

preHandle():在请求方法执行前被调用,也就是调用目标方法之前被调用。比如我们在操作数据之前先要验证用户的登录信息,就可以在此方法中实现,如果验证成功则返回 true,继续执行数据操作业务;否则就返回 false,后续操作数据的业务就不会被执行了。

postHandle():顾名思义就是在当前请求进行处理之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用。所以,我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。postHandle 方法被调用的方向跟 preHandle 是相反的。也就是说,先声明的 Interceptor 的 postHandle 方法反而会后执行

afterCompletion():该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。

定义两个拦截器AInterceptor,BInterceptor如下所示:

java 复制代码
@Slf4j
@Component
public class AInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("A拦截器preHandle()方法执行了");
        // 为ture放行执行后续逻辑
        return true;
    }
​
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("A拦截器postHandle()方法执行了");
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("A拦截器afterCompletion()方法执行了");
    }
}
java 复制代码
@Component
@Slf4j
public class BInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("B拦截器preHandle()方法执行了");
        // 返回ture放行执行后续逻辑
        return true;
    }
​
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("B拦截器postHandle()方法执行了");
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("B拦截器afterCompletion()方法执行了");
    }
}

添加拦截器配置类实现WebMvcConfigurer

typescript 复制代码
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Resource
    private AInterceptor aInterceptor;
    @Resource
    private BInterceptor bInterceptor;
​
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // registry.addInterceptor(new AInterceptor()); new 拦截器会导致拦截器不能成功依赖注入bean
        registry.addInterceptor(bInterceptor).addPathPatterns("/**").excludePathPatterns("/user/**").order(2);
        registry.addInterceptor(aInterceptor).addPathPatterns("/**").excludePathPatterns("/user/**").order(1);
    }
}

postman访问接口:http://127.0.0.1:8080/test/filter,控制台日志打印如下。

perl 复制代码
INFO com.shepherd.basedemo.interceptor.AInterceptor preHandle [http-nio-8080-exec-1@13504] : A拦截器preHandle()方法执行了
INFO com.shepherd.basedemo.interceptor.BInterceptor preHandle [http-nio-8080-exec-1@13504] : B拦截器preHandle()方法执行了
INFO com.shepherd.basedemo.controller.TestController testFilter [http-nio-8080-exec-1@13504] : controller业务方法执行了
INFO com.shepherd.basedemo.interceptor.BInterceptor postHandle [http-nio-8080-exec-1@13504] : B拦截器postHandle()方法执行了
INFO com.shepherd.basedemo.interceptor.AInterceptor postHandle [http-nio-8080-exec-1@13504] : A拦截器postHandle()方法执行了
INFO com.shepherd.basedemo.interceptor.BInterceptor afterCompletion [http-nio-8080-exec-1@13504] : B拦截器afterCompletion()方法执行了
INFO com.shepherd.basedemo.interceptor.AInterceptor afterCompletion [http-nio-8080-exec-1@13504] : A拦截器afterCompletion()方法执行了

拦截器执行顺序默认是按照添加进拦截器链的顺序,如果要设置顺序请自定义order,但是请注意在拦截器组件上使用注解@Order是无效的:

less 复制代码
@Slf4j
@Component
@Order(2)
public class AInterceptor implements HandlerInterceptor {
}

3.2 实现原理

我们都知道一个请求到来时先会使用组件HandlerMapping去匹配Controller的方法(Handler)和符合拦截路径的Interceptor

Handler和多个Interceptor被封装成一个拦截器链HandlerExecutionChain的对象,最后来到DispatcherServlet的doDispatch方法中执行拦截器

java 复制代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response){
  //根据请求信息获得HandlerExecutionChain
  HandlerExecutionChain mappedHandler = this.getHandler(request);
  //获得处理器适配器
  HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
  //执行Interceptor的前置方法,前置方法如果返回false,则该流程结束
  if (!mappedHandler.applyPreHandle(request, response)) {
    return;
  }
  //执行handler,一般是HandlerMethod
  ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  //执行后置方法
  mappedHandler.applyPostHandle(processedRequest, response, mv);
  //执行最终方法
  this.triggerAfterCompletion(processedRequest, response, mappedHandler, e);
}

来到HandlerExecutionChainapplyPreHandle方法源码:

kotlin 复制代码
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
  //对interceptorList进行遍历,正向遍历,与此同时使用interceptorIndex进行计数
  for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
    //取出每一个Interceptor对象
    HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
    //调用Interceptor的preHandle方法,如果返回false,则直接执行Interceptor的最终方法
    if (!interceptor.preHandle(request, response, this.handler)) {
      //执行Interceptor的最终方法
      this.triggerAfterCompletion(request, response, (Exception)null);
      return false;
    }
  }
  return true;
}

跟踪 HandlerExecutionChainapplyPostHandle方法源码:

less 复制代码
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
  //对interceptorList进行遍历,逆向遍历
  for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
  //取出每一个Interceptor
  HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
  //执行Interceptor的postHandle方法
  interceptor.postHandle(request, response, this.handler, mv);
  }
}

跟踪HandlerExecutionChaintriggerAfterCompletion方法源码:

java 复制代码
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
  //逆向遍历interceptorList,遍历的个数为执行的applyPreHandle次数-1
  for(int i = this.interceptorIndex; i >= 0; --i) {
    //取出每一个Interceptor
    HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
    try {
      //执行Interceptor的afterCompletion方法
      interceptor.afterCompletion(request, response, this.handler, ex);
    } catch (Throwable var7) {
      logger.error("HandlerInterceptor.afterCompletion threw exception", var7);
    }
  }
}

applyPostHandle()triggerAfterCompletion()都是按照拦截器链逆序遍历,这也就验证上面所说的postHandle()preHandle()执行顺序相反的结论

拦截器执行流程图如下:

项目推荐:基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企业级系统架构底层框架封装,解决业务开发时常见的非功能性需求,防止重复造轮子,方便业务快速开发和企业技术栈框架统一管理。引入组件化的思想实现高内聚低耦合并且高度可配置化,做到可插拔。严格控制包依赖和统一版本管理,做到最少化依赖。注重代码规范和注释,非常适合个人学习和企业使用

Github地址github.com/plasticene/...

Gitee地址gitee.com/plasticene3...

微信公众号Shepherd进阶笔记

交流探讨qun:Shepherd_126

4.过滤器、拦截器、@Aspect切面执行顺序和区别

4.1 执行顺序

基于上面过滤器和拦截器的使用示例,这里新增一个日志切面来看看执行顺序:

less 复制代码
@Aspect
@Component
@Slf4j
public class LogAspect {
​
    @Around("execution(* com.shepherd..controller..*(..))") //切入点,拦截com.shepherd..controller下所以方法
    public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("执行@Aspect切面方法了");
         return joinPoint.proceed();
    }
​
}

调上面测试接口控制台输出如下:

less 复制代码
​
2024-05-11 11:00:47.149 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.filter.AFilter doFilter [http-nio-8080-exec-1@17319] : 过滤器A: 执行doFilter方法了
2024-05-11 11:00:47.149 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.filter.BFilter doFilter [http-nio-8080-exec-1@17319] : 过滤器B: 执行doFilter方法了
2024-05-11 11:00:47.160 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.interceptor.AInterceptor preHandle [http-nio-8080-exec-1@17319] : A拦截器preHandle()方法执行了
2024-05-11 11:00:47.160 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.interceptor.BInterceptor preHandle [http-nio-8080-exec-1@17319] : B拦截器preHandle()方法执行了
2024-05-11 11:00:47.175 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.interceptor.LogAspect timeAround [http-nio-8080-exec-1@17319] : 执行@Aspect切面方法了
2024-05-11 11:00:47.189 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.controller.TestController testFilter [http-nio-8080-exec-1@17319] : controller业务方法执行了
2024-05-11 11:00:47.211 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.interceptor.BInterceptor postHandle [http-nio-8080-exec-1@17319] : B拦截器postHandle()方法执行了
2024-05-11 11:00:47.212 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.interceptor.AInterceptor postHandle [http-nio-8080-exec-1@17319] : A拦截器postHandle()方法执行了
2024-05-11 11:00:47.212 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.interceptor.BInterceptor afterCompletion [http-nio-8080-exec-1@17319] : B拦截器afterCompletion()方法执行了
2024-05-11 11:00:47.212 ShepherddeMacBook-Pro.local [common-demo] [] INFO com.shepherd.basedemo.interceptor.AInterceptor afterCompletion [http-nio-8080-exec-1@17319] : A拦截器afterCompletion()方法执行了

结论:执行顺序:过滤器→拦截器→@Aspect→@Controller业务处理

过滤器、拦截器、@Aspect切面都是aop思想的一种体现,具体使用选择的话建议根据项目需要,参考它们的特性及执行顺序,选择最合适的一个或多个进行使用,个人觉得三者几乎都能实现相同的需求功能,比如接口权限检验,参数日志记录等等

4.2 区别

过滤器(Filter)、拦截器(Interceptor)和@Aspect切面(Aspect)是在Java Web开发中用于处理请求的三种不同类型的组件。它们在功能、使用场景和执行时机等方面有所不同,下面是它们的区别:

  1. 功能

    • 过滤器主要用于过滤和修改HTTP请求和响应 ,可以在请求到达Servlet之前和响应返回客户端之前对请求和响应进行处理,如设置字符编码、进行身份认证 、请求重定向等。个人认为需要修改HTTP请求和响应,或者需要及早判断的逻辑,例如在业务想系统登录认证,设置请求头traceId使用过滤器比较合适
    • 拦截器主要用于在请求到达Controller之前和视图渲染之后进行处理,可以在请求处理前进行一些预处理,如接口菜单权限验证等,也可以在请求处理后进行一些后处理,如日志记录、数据封装等。
    • @Aspect切面主要用于在特定的连接点上执行一些操作,可以在方法执行前、执行后、抛出异常时等时机执行一些逻辑,如事务管理、日志记录、性能监控等。
  2. 使用场景

    • 过滤器通常用于在Servlet容器级别对请求进行过滤和处理,例如在web.xml中配置的过滤器用于全局性的请求过滤,如字符编码过滤器、权限验证过滤器等。
    • 拦截器通常用于在Spring MVC框架中对请求进行预处理和后处理,可以针对特定的Controller或URL进行配置,并可以灵活地控制拦截的范围和时机。
    • @Aspect切面通常用于面向切面编程(AOP),通过定义切面来提取应用中横切关注点的通用逻辑,例如事务管理、日志记录、安全检查等。
  3. 执行时机

    • 过滤器在请求到达Servlet之前和响应返回客户端之前执行。
    • 拦截器在请求到达Controller之前和视图渲染之后执行。
    • @Aspect切面在特定的连接点上执行,可以在方法执行前、执行后、抛出异常时等时机执行一些逻辑。

5.总结

过滤器和拦截器都是在Java Web开发中常用的组件,用于对请求和响应进行预处理和后处理。它们在作用范围、生命周期管理和处理方式等方面有所不同,开发人员可以根据具体的业务需求选择合适的组件来实现相应的功能。

相关推荐
Asthenia04121 分钟前
理解词法分析与LEX:编译器的守门人
后端
uhakadotcom2 分钟前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
Asthenia04121 小时前
Spring扩展点与工具类获取容器Bean-基于ApplicationContextAware实现非IOC容器中调用IOC的Bean
后端
bobz9651 小时前
ovs patch port 对比 veth pair
后端
Asthenia04121 小时前
Java受检异常与非受检异常分析
后端
uhakadotcom2 小时前
快速开始使用 n8n
后端·面试·github
JavaGuide2 小时前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
bobz9652 小时前
qemu 网络使用基础
后端
Asthenia04122 小时前
面试攻略:如何应对 Spring 启动流程的层层追问
后端
Asthenia04123 小时前
Spring 启动流程:比喻表达
后端