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

news2024/11/24 14:32:47

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

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

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

相关文章

PaddleOCR_PP-Structure

静态IP设置 # 修改网卡配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33# 修改文件内容 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic IPADDR192.168.15.132 NETMASK255.255.255.0 GATEWAY192.168.15.2 DEFROUTEyes IPV4_FAILURE_FATALno IPV6INIT…

分布式锁之RedissonLock

什么是Redisson&#xff1f; 俗话说他就是看门狗&#xff0c;看门狗机制是一种用于保持Redis连接活跃性的方法&#xff0c;通常用于分布式锁的场景。看门狗的工作原理是&#xff1a;当客户端获取到锁之后&#xff0c;会对Redis中的一个特定的键设置一个有限的过期时间&#xff…

易我分区大师18.5发布上线:全方位提升您的磁盘管理体验

近期&#xff0c;易我分区大师18.5版本正式发布上线&#xff01; 新版本在原有基础上进行了升级和优化&#xff0c;不仅继承了前版本的强大功能&#xff0c;还新增了C盘数据迁移、清除Windows PIN码以及蓝屏问题助手等实用功能&#xff0c;帮助用户更轻松、更高效地管理电脑磁…

车载电子电器架构 —— UDS Service 11介绍

车载电子电器架构 —— UDS Service 11介绍 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证…

栈的相关操作练习:用栈实现队列

1.思路解析 首先了解&#xff0c;队列遵循先进先出&#xff0c;栈遵循后进先出&#xff0c;所以利用两个栈popst与pushst进行元素转移后可以实现先进先出的功能。原题来源于leetcode中的:232.用队列实现栈 2.操作详解 首先要自己写一个栈及其操作&#xff0c;这里直接给出&…

C++类与对象基础探秘系列(一)

目录 面向过程和面向对象初步认识 类的引入 类的定义 类的访问限定符及封装 访问限定符 封装 类的作用域 类的实例化 类的对象模型 如何计算类对象的大小 类对象的存储方式 结构体内存对齐规则 类成员函数的this指针 this指针的引出 this指针的特性 C语言和C实…

leetCode-hot100-位运算专题

例题中的视频讲解是B站中的宝藏博主的讲解视频&#xff0c;每道题后面都附有该题对应的视频链接~ 位运算知识总结 1.异或2.与运算和或运算3.左移和右移4.综合例题 1.异或 参考资料&#xff1a;位运算-异或&#xff0c;以下知识点讲解的内容参考了该篇博文&#xff0c;有兴趣的…

MySQL表结构的一些设计经验分享

我们在对一张表进行设计时&#xff0c;还要遵守一些基本的原则&#xff0c;比如经常听见的“范式准则”。但范式准则过于理论&#xff0c;在真实业务中&#xff0c;不必严格遵守三范式的要求。而且有时为了性能考虑&#xff0c;还可以进行反范式的设计&#xff0c;比如在数据仓…

ATFX:美国通胀率平台期,或助力黄金延续涨势

ATFX金属&#xff1a;5月9日19:00至5月10日19:00&#xff0c;COMEX黄金的小时级别出现一波持续24小时的上涨走势&#xff0c;最高触及2385.3美元&#xff0c;累计涨幅2.78%&#xff0c;成为上周最佳的短线交易时机。R阻力线形成后&#xff0c;COMEX黄金进入下降通道&#xff0c…

采油厂职工向媒体投稿的好方法找到了

作为一名采油厂的职工,我深知在媒体上定期投稿的重要性。这不仅是我们展示工作成果、传播企业文化的重要途径,更是上级考核我们工作表现的一项指标。然而,在投稿的过程中,我经历了不少心酸与困扰。 起初,我采用传统的邮箱投稿方式。每天,我都会花费大量时间在网络上搜索合适的媒…

ms17-010(永恒之蓝)

1.漏洞介绍: 永恒之蓝&#xff08;ms17-010&#xff09;爆发于2017年4月14日晚&#xff0c;是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限&#xff0c;以此来控制被入侵的计算机。甚至于2017年5月12日&#xff0c; 不法分子通过改造“永恒之蓝”制作了wannacry勒索病…

n的阶乘(函数)(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//声明n的阶乘函数fac; int fac(int n);int main() {//初始化变量值&#xff1b;int n, result;//获取要阶乘的数&#xff1b;printf("请输入要阶乘的n&…

创新点!CNN与LSTM结合,实现更准预测、更快效率、更高性能!

推荐一个能发表高质量论文的好方向&#xff1a;LSTM结合CNN。 LSTM擅长捕捉序列数据中的长期依赖关系&#xff0c;而CNN则擅长提取图像数据的局部特征。通过结合两者的优势&#xff0c;我们可以让模型同时考虑到数据的时序信息和空间信息&#xff0c;减少参数降低过拟合风险&a…

怎么通过微信小程序实现远程控制8路控制器/断路器

怎么通过微信小程序实现远程控制8路控制器/断路器呢&#xff1f; 本文描述了使用微信小程序调用HTTP接口&#xff0c;实现控制8路控制器/断路器&#xff0c;支持8路输出&#xff0c;均可独立控制&#xff0c;可接入各种电器。 可选用产品&#xff1a;可根据实际场景需求&#…

【2024】Gradle安装配置以及基于Kotlin 进行详细使用

目录&#x1f4bb; 一、介绍二、安装Gradle1、下载安装2、配置环境变量2.1、mac2.2、windows 3、配置国内国内镜像源4、初始化Gradle项目4.1、项目结构4.2、Gradle常用命令 三、项目配置1、配置文件介绍1.1、设置文件settings.gradle1.1.1、单体项目1.1.2、父子项目 1.2、构建文…

车载电子电器架构 —— Vector对于车载以太网的解决方案(协议栈)

车载电子电器架构 —— Vector对于车载以太网的解决方案(协议栈) 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你…

电子学会C/C++编程等级考试2024年03月(一级)真题解析

C/C++编程(1~8级)全部真题・点这里 第1题:倒序输出 依次输入4个整数a、b、c、d,将他们倒序输出,即依次输出d、c、b、a这4个数。 时间限制:1000 内存限制:65536 输入 一行4个整数a、b、c、d,以空格分隔。 0 < a,b,c,d < 108 输出 一行4个整数d、c、b、a,整数之间以…

探索人员定位系统的架构设计

随着科技的不断发展&#xff0c;人员定位系统在各个领域中扮演着越来越重要的角色。从室内定位到室外定位&#xff0c;从个人安全到物流管理&#xff0c;人员定位系统为我们提供了精准的位置信息&#xff0c;极大地促进了工作效率和安全管理。 但是&#xff0c;一个成功的人员…

docker 安装 Redis (附图文教程)

首先确保已安装docker 安装docker 拉取 redis 镜像 搜索镜像 docker search redis使用最多人使用的 拉取镜像 没有指定版本默认最新版本 docker pull redis查看镜像 docker images启动容器 创建挂载目录 mkdir -p /home/local/redis/conf /home/local/redis/data创建…

[算法][BFS][leetcode]994. 腐烂的橘子

题目地址 https://leetcode.cn/problems/rotting-oranges/description/ 错误解法 class Solution {public int orangesRotting(int[][] grid) {//层序遍历int ans 0;for (int i 0;i<grid.length;i) {for(int j 0;j<grid[0].length;j){boolean flag false;if(grid[i][j…