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

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定义:

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

@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方法了");
    }
}
@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方法了");
    }
}

添加接口进行测试:

@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {

    @GetMapping("/filter")
    public void testFilter() {
        log.info("controller业务方法执行了");
    }

}

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

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,控制台日志打印如下。

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

@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(),重启调接口结果如下:

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()

 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() 方法。

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 接口即可:

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如下所示:

@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()方法执行了");
    }
}
@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

@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,控制台日志打印如下。

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是无效的:

@Slf4j
@Component
@Order(2)
public class AInterceptor implements HandlerInterceptor {
}

3.2 实现原理

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

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

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方法源码:

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方法源码:

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方法源码:

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地址:https://github.com/plasticene/plasticene-boot-starter-parent

Gitee地址:https://gitee.com/plasticene3/plasticene-boot-starter-parent

微信公众号Shepherd进阶笔记

交流探讨qun:Shepherd_126

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

4.1 执行顺序

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

@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();
    }

}

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

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开发中常用的组件,用于对请求和响应进行预处理和后处理。它们在作用范围、生命周期管理和处理方式等方面有所不同,开发人员可以根据具体的业务需求选择合适的组件来实现相应的功能。

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

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

相关文章

【Qt】demo示例--通过定时器实现时间刷新

【Qt】demo示例--通过定时器实现时间刷新 1.背景2.代码3.运行 1.背景 Qt Creator版本&#xff1a;4.2.0 &#xff0c;如下图&#xff1a; 即安装qt-opensource-windows-x86-msvc2013_64-5.7.1.exe 后自带得Qt编程IDE&#xff1b; 2.代码 项目结构如下&#xff1a; mydial…

Navicat Premium数据库新建函数报错问题

1.问题产生原因&#xff1a; (1)可能是软件 Navicat Premium 的版本不一样 (2)可能是与某些字符集不一样 2.找出解决办法&#xff1a; (1)参考对照系统自带sys数据库表的函数找出的区别&#xff08;后面遇到同样问题可以这样排查&#xff09; (2)不加 DETERMINISTIC 声明&a…

js图片回显的方法

直接上代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body>// HTML部分<input type"file" id"fileInput"><button onclick"show…

Nginx内网环境开启https

文章目录 前言一、open-ssl1. 验证2. 安装3.生成ssl证书 一、nginx1. 验证支持模块2. 安装必要模块2.1 重新编译nginx2.2 替换原文件 3. 配置https 总结 前言 nginx开启https前提&#xff1a; 服务器支持open-sslnginx 包含--with-http_ssl_module --with-stream --with-stre…

融知财经:期货和现货的区别是什么?哪个风险大?

期货和现货在交易对象等方面存在明显的区别。期货交易是一种衍生金融工具&#xff0c;主要用于价格发现、风险管理和投机&#xff0c;而现货交易则是商品和服务的实际买卖。在选择进行期货交易还是现货交易时&#xff0c;投资者需要根据自己的需求和市场情况来决定。 期货和现货…

In Context Learning(ICL)个人记录

In Context Learning&#xff08;ICL&#xff09;简介 In Context Learning&#xff08;ICL&#xff09;的关键思想是从类比中学习。上图给出了一个描述语言模型如何使用 ICL 进行决策的例子。首先&#xff0c;ICL 需要一些示例来形成一个演示上下文。这些示例通常是用自然语言…

C++牛客周赛题目分享(2)小红叕战小紫,小红的数组移动,小红的素数合并,小红的子序列求和

目录 ​编辑 1.前言 2.四道题目 1.小红叕战小紫 1.题目描述 2.输入描述 3.输出描述 4.示例 5.题解与思路 2.小红的数组移动 1.题目描述 2.输入描述 3.输出描述 4.示例 5.题解与思路 3.小红的素数合并 1.题目描述 2.输入描述 3.输出描述 4.示例 5.题解与思…

华东政法大学公布2024《负面清单期刊目录》,附目录

近日&#xff0c;华东政法大学公布2024《负面清单期刊目录》&#xff0c;包括《齐齐哈尔大学学报(哲学社会科学版)》《景德镇学院学报(社科)》《九江学院学报(社科)》《北京印刷学院学报》《江西电力职业技术学院学报》《中国多媒体与网络教学学报》《吉林省教育学院学报》《开…

pyqt动画效果放大与缩小

pyqt动画效果放大与缩小 QPropertyAnimation介绍放大与缩小效果代码 QPropertyAnimation介绍 QPropertyAnimation 是 PyQt中的一个类&#xff0c;它用于对 Qt 对象的属性进行动画处理。通过使用 QPropertyAnimation&#xff0c;你可以平滑地改变一个对象的属性值&#xff0c;例…

【新手入门】Github与Git使用教程

Github与Git 一、Github基础教程 1.1 基本操作 点击代码文件可以直接查看文件的内容&#xff0c;支持在线修改文件&#xff0c;只需要点击(文件内容)右上角的编辑按钮即可进行编辑。 README.md一般介绍项目的功能&#xff0c;用法&#xff0c;注意事项&#xff1b;有时还有…

二、安装、使用Grafana

目录 一、安装Grafana 二、使用grafana 一、安装Grafana 官网&#xff1a;https://grafana.com/ 账号&#xff1a;admin 密码&#xff1a;xxxxxx [rootrabbitmq_2 prometheus]# [rootrabbitmq_2 prometheus]# wget https://dl.grafana.com/enterprise/release/grafana-enter…

kubernetes集群开启ipvs模式

1&#xff09; 需要在所有节点机器安装ipvsadm&#xff1a; apt install ipvsadm 2) 加载ipvs模块 modprobe ip_vs modprobe ip_rr modprobe ip_wrr modprobe ip_sh修改k8s集群内的kube-proxy cm kubectl edit cm kube-proxy -n kube-system修改模式为ipvs&#xff1a; 如图 …

重大升级 | OpenSCA SaaS全面接入供应链安全情报!

结合社区用户反馈及研发小伙伴的积极探索&#xff0c; OpenSCA 项目组再次发力&#xff0c;SaaS版本重大升级啦&#xff01; 用户的需求是OpenSCA前进的动力&#xff0c;欢迎更多感兴趣的朋友们积极试用和反馈~ 更 新 内 容 1.全面接入云脉XSBOM供应链安全情报 2.强大的资产…

太速科技-基于VU3P的双路100G光纤加速计算卡

基于VU3P的双路100G光纤加速计算卡 一、板卡概述 基于Xilinx UltraScale16 nm VU3P芯片方案基础上研发的一款双口100 G FPGA光纤以太网PCI-Express v3.0 x16智能加速计算卡&#xff0c;该智能卡拥有高吞吐量、低延时的网络处理能力以及辅助CPU进行网络功能卸载的能力…

【董晓算法】竞赛常用知识之字符串2

前言&#xff1a; 本系列是学习了董晓老师所讲的知识点做的笔记 董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com) 动态规划系列&#xff08;还没学完&#xff09; 【董晓算法】动态规划之线性DP问题-CSDN博客 【董晓算法】动态规划之背包DP问题&#xff…

通往糊涂之路 The road to serfdom

最近被推送了一本书&#xff0c;哈耶克的............ 试一试&#xff0c;看看能不能看懂&#xff0c;也许是通往糊涂之路。

AI游戏外挂为何违法?

尊敬的读者们&#xff0c;大家好&#xff01;今天我想和大家探讨一个备受争议的话题——AI游戏外挂的合法性。近年来&#xff0c;随着人工智能技术的飞速发展&#xff0c;AI外挂逐渐成为游戏领域的一大毒瘤。那么&#xff0c;为什么AI游戏外挂会被视为违法行为呢&#xff1f;本…

vue3延迟加载(异步组件​)defineAsyncComponent

最简单用法 Index.vue: <script setup> import { onMounted, defineAsyncComponent } from vue import ./index.cssconst Child defineAsyncComponent(() > import(./Child.vue))onMounted(() > {}) </script><template><div class"m-home-w…

前端XHR请求数据

axios封装了XHR(XMLHttpRequest) 效果 项目结构 Jakarta EE9&#xff0c;Web项目。 无额外的maven依赖 1、Web页面 index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title&…

网络编程套接字详解

目录 1. 预备介绍 2.网络字节序 3.udp网络程序 4.地址转换函数 5.udp网络编程 1.预备介绍 1.1源IP地址和目标IP地址 举个例子: 从北京出发到上海旅游, 那么源IP地址就是北京, 目标IP地址就是上海. 1.2 端口号 作用: 标识一个进程, 告诉OS这个数据交给那个进程来处理; (1)…