写点什么

Spring 常用的三种拦截器详解

  • 2024-07-29
    福建
  • 本文字数:2963 字

    阅读完需:约 10 分钟

前言


在开发过程中,我们常常使用到拦截器来处理一些逻辑。最常用的三种拦截器分别是 AOP、 Interceptor 、 Filter,但其实很多人并不知道什么时候用 AOP,什么时候用 Interceptor,什么时候用 Filter,也不知道其拦截顺序,内部原理。今天我们详细介绍一下这三种拦截器。


拦截器顺序


我们现在有一个 controller 接口,叫做 test,现在我们在项目有定义了三个拦截器,其顺序如下


Filter(before) > Interceptor(before) > AOP(before) > test 方法 > AOP(after) > Interceptor(after) > Filter(after)


具体流程如下两幅图片。




所以有时候我们使用不了 AOP/Interceptor, 只能使用 Filter。


  • 比如我们现在是一个 Get 请求,但是别人却发送了一个 Post 请求,这时候只有 Filter 才能拦截,只能使用 Filter.

  • 又比如我们在 Interceptor 获取了请求参数以后,因为是一个流,后面 controller 就会获取不到,我们一般会采用包装类来实现重复读取。但假如我们直接使用 AOP 就完全可以避免这个问题了。


类似的例子比较多,所以我们应该知其然,也要知其所以然。下面我们介绍一下三种拦截器的具体用法。


Filter 拦截器


filter 是 servlet 层面的提供拦截器,和 spring 无关。只是说现在 spring/springboot 一统江湖,很多项目在 spring 的基础上面使用 filter。那我们如果在 spring 项目中使用 filter 拦截器呢

@Slf4jpublic class FirstFilter implements Filter {	@Override	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("FirstFilter before doFilter"); filterChain.doFilter(servletRequest, servletResponse); }}
@Slf4jpublic class SecondFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("SecondFilter before doFilter"); filterChain.doFilter(servletRequest, servletResponse); }}
@Slf4j@Configurationpublic class FilterConfig {
@Bean public FilterRegistrationBean firstFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new FirstFilter()); registration.addUrlPatterns("/*"); registration.setName("FirstFilter"); // 数字越小,优先级越高 registration.setOrder(1); return registration; }
@Bean public FilterRegistrationBean secondFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new SecondFilter()); registration.addUrlPatterns("/*"); registration.setName("SecondFilter"); // 数字越大,优先级越低 registration.setOrder(2); return registration; }
}
复制代码


输出结果


FirstFilter before doFilter

SecondFilter before doFilter


Interceptor 拦截器


Interceptor 是 springmvc 给我们提供的拦截器,只有在 sotingmvc 中才可以使用

@Slf4j@Componentpublic class FirstInterceptor implements HandlerInterceptor {
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("FirstInterceptor preHandle"); return true; }}
@Slf4j@Componentpublic class SecondInterceptor implements HandlerInterceptor {
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("SecondInterceptor preHandle"); return true; }
}
@Configurationpublic class InterceptorConfig implements WebMvcConfigurer {
@Autowired private FirstInterceptor firstInterceptor;
@Autowired private SecondInterceptor secondInterceptor;
// 配置拦截规则 public void addInterceptors(InterceptorRegistry registry) {
// 按照注册的顺序,依次执行 registry.addInterceptor(firstInterceptor) .addPathPatterns("/**") .excludePathPatterns("/task/**");
registry.addInterceptor(secondInterceptor) .addPathPatterns("/**") .excludePathPatterns("/spring/**"); }}
复制代码


输出结果


FirstInterceptor preHandle

SecondInterceptor preHandle


AOP 拦截器


AOP 是我们非常常用的拦截器,织入点,有 before,after,around 等,我们今天以 around 为例

@Aspect@Componentpublic class LoggerAOP {
@Pointcut("execution (public * com.xxx.controller..*(..))") public void pointcutLogger() {}
@Around("pointcutLogger()") public Object methodAround(ProceedingJoinPoint joinPoint) throws Throwable { try { logger.info("request className = {}, method = {}, ip = {}, param = {}", className, methodName, ip, param);
resp = joinPoint.proceed();
long duration = stopwatch.elapsed(TimeUnit.MILLISECONDS); logger.info("response className = {}, method = {}, resp = {}, cost = {}ms", className, methodName, buildResp(resp), duration); }
catch (Throwable e) { logger.error("logger request className = {}, method = {} fail message = {} ", className, methodName, e.getMessage(), e); throw e; } return resp; }}
复制代码


最后


我们介绍了一下 spring 中,常用的三种拦截器,以及他们在 springboot 中如何使用。


最后给大家出一个小作业,如果把上面三个拦截器放到一个项目中,他会输出什么顺序呢?

[INFO  2024-07-27 16:01:52.146] [http-nio-8099-exec-2] [] - [FirstFilter.doFilter:17] [FirstFilter before doFilter][INFO  2024-07-27 16:01:52.146] [http-nio-8099-exec-2] [] - [SecondFilter.doFilter:17] [SecondFilter before doFilter][INFO  2024-07-27 16:01:52.148] [http-nio-8099-exec-2] [] - [FirstInterceptor.preHandle:18] [FirstInterceptor preHandle][INFO  2024-07-27 16:01:52.148] [http-nio-8099-exec-2] [] - [SecondInterceptor.preHandle:18] [SecondInterceptor preHandle][INFO  2024-07-27 16:01:52.148] [http-nio-8099-exec-2] [cdc644d0-afdf-4283-bf52-fc5fdd217746] - [LoggerAOP.methodAround:52] [request className = TestController, method = testController, ip = 0:0:0:0:0:0:0:1, param = [null]]
[INFO 2024-07-27 16:01:52.149] [http-nio-8099-exec-2] [cdc644d0-afdf-4283-bf52-fc5fdd217746] - [TestController.testController:57] [main process]
[INFO 2024-07-27 16:01:52.149] [http-nio-8099-exec-2] [cdc644d0-afdf-4283-bf52-fc5fdd217746] - [LoggerAOP.methodAround:62] [response className = TestController, method = testController, resp = "ok", cost = 0ms]
复制代码


文章转载自:程序员博博

原文链接:https://www.cnblogs.com/wenbochang/p/18327158

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
Spring 常用的三种拦截器详解_Java_快乐非自愿限量之名_InfoQ写作社区