简介
说明
本文介绍SpringMVC(SpringBoot)中的过滤器、拦截器、AOP的区别及其用法。
过滤器/拦截器/AOP
概述
简介
- 过滤器:Filter;拦截器:Interceptor 。Spring中,建议优先用拦截器(Filter 能做的,interceptor 几乎都能更简单)。
- AOP:可以自定义切入的点,有方法的参数,但拿不到http请求,可以通过RequestContextHolder等方式获得。
AOP用法见:Spring之AOP系列–使用/实例 – 自学精灵
执行顺序概述
过滤前=> 拦截前=> ControllerAdvice=> AOP=> Controller=> AOP=> ControllerAdvice => 拦截后=> 过滤后

执行顺序详解
- 不抛异常时
- 监听器 => 过滤器=> 拦截器(前处理)=> AOP(前处理)=> @ControllerAdvice的@ModelAttribute=> 自己的controller=> AOP(后处理)=> @ControllerAdvice实现beforeBodyWrite接口=> 拦截器(后处理)
- 抛异常时:
- 监听器 => 过滤器=> 拦截器(前处理)=> AOP(前处理)=> @ControllerAdvice的@ModelAttribute=> 自己的controller=> AOP(后处理)=> @ControllerAdvice的@ExceptionHandler=> @ControllerAdvice实现beforeBodyWrite接口=> 拦截器(后处理)。
过滤器与拦截器的区别
| 项 | 过滤器 | 拦截器 |
| 使用场景 | 对请求/响应进行修改、判断等。一般用于过滤参数、登录权限验证、资源访问权限控制、敏感词汇过滤、字符编码转换。 | 在service或者一个方法前/后调用一个方法,或者在方法后调用一个方法。 |
| 执行时机 | 请求时:进入容器后,servlet之前进行处理。 响应时:在servlet之后处理。 | 请求时:进入servlet后,进入Controller前进行处理。 响应时:在Controller后,servlet前进行处理。 |
| 能力 | 可以拿到原始的http请求与响应,拿不到请求的控制器和请求控制器中的方法的信息。 可以修改请求、响应、参数:比如:修改字符编码、删除低俗文字、删除危险字符、修改参数 | 可以拿到你请求的控制器和方法,也能拿到请求与响应。 |
| 作用范围 | 所有请求。 | 只能处理controller请求。 无法控制静态资源。 |
| 顺序 | 可指定顺序。 | 可指定顺序。 |
| 实现方式 | 回调函数(同步调用) | AOP |
| 使用范围 | 只能用于Web。 (过滤器实现 javax.servlet.Filter 接口(属于Servlet),所以过滤器要依赖于Tomcat等容器,只能在web程序中使用。) | 可用于Web、Application、Swing。 (由Spring提供,不依赖Tomcat等容器,可单独使用。) |
| 灵活性/粒度 | 灵活性差(粒度大)。 不能使用 Spring 容器资源。 | 灵活性好(粒度小)。 能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可。 |
| 打断链路 | 打断方法:请求和响应引发中断,需要额外动作,比如重定向到错误页面。 | 打断方法:preHandle方法内返回 false |
过滤器
见:SpringBoot–过滤器(Filter)–使用/教程/实例 – 自学精灵
拦截器
见:SpringBoot–拦截器(Interceptor)–使用/教程/实例 – 自学精灵
联合实例:过滤/拦截/@ControllerAdvice/AOP
代码
实体类
package com.example.entity;
import lombok.Data;
@Data
public class User {
private Integer id;
private String name;
private Integer age;
}
控制器
package com.example.controller;
import com.example.entity.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/test1")
public User test1(User user) {
System.out.println("HelloController.test1");
return user;
}
}
过滤器
package com.example.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class MyFilter implements Filter {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter.init");
System.out.println(" 过滤器名:" + filterConfig.getFilterName());
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
// do something 处理request 或response
System.out.println("MyFilter.doFilter");
if (servletRequest instanceof HttpServletRequest) {
System.out.println(" URL:" + ((HttpServletRequest)servletRequest).getRequestURL());
}
// 调用filter链中的下一个filter
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("MyFilter.destroy");
}
}
package com.example.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean registrationBean1() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
filterRegistrationBean.addUrlPatterns("/*");
// filterRegistrationBean.setName("xxx"); //可设置过滤器名字
return filterRegistrationBean;
}
}
拦截器
package com.example.interceptor;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("MyInterceptor.preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, @Nullable ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor.postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, @Nullable Exception ex) throws Exception {
System.out.println("MyInterceptor.afterCompletion");
}
}
package com.example.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor());
}
}
@ControllerAdvice
package com.example.advice;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
@ControllerAdvice
public class GlobalControllerAdvice {
@ModelAttribute
public void authenticationUser() {
System.out.println("GlobalControllerAdvice.authenticationUser");
}
}
对@RequestMapping的AOP
package com.example.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class RequestMappingAspect {
@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void pointcut() {
}
@Around("pointcut()")
public Object around1(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[RequestMappingAspect.around1]: around before");
Object object = joinPoint.proceed();
System.out.println("[RequestMappingAspect.around1]: around after");
return object;
}
}
测试
启动SpringBoot
MyFilter.init 过滤器名:myFilter
postman访问:http://localhost:8080/hello/test1?name=Tony
后端结果
MyFilter.doFilter URL:http://localhost:8080/hello/test1 MyInterceptor.preHandle GlobalControllerAdvice.authenticationUser [RequestMappingAspect.around1]: around before HelloController.test1 [RequestMappingAspect.around1]: around after MyInterceptor.postHandle MyInterceptor.afterCompletion

postman结果
{
"id": null,
"name": "Tony",
"age": null
}
关闭SpringBoot
Disconnected from the target VM, address: '127.0.0.1:61631', transport: 'socket' MyFilter.destroy 2020-09-05 17:49:01.499 INFO 17144 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'

请先 !