过滤器和拦截器都是日常开发中经常使用到的技术,他们都可以对特定的请求进行增强处理,比如在请求之前或之后插入自定义的代码,完成想要的功能。过滤器和拦截器最本质的区别是,过滤器是在请求到达servlet
之前执行,拦截器则在请求到达servlet
之后执行。需要注意的是,SpringBoot
中的拦截器依赖于SpringBoot
容器,而过滤器是servlet
本身提供的。
过滤器的实现
过滤器依赖servlet
中的Filter
接口,自定义一个Filter
的实现类,重写doFilter()
方法
java 代码解读复制代码@Component
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("hello");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("world");
}
}
filterChain.doFilter(servletRequest,servletResponse);
这个方法的作用是放行请求,在这条语句的前后可以做一些自定义的操作,比如记录日志、定义请求和返回的字符集编码、或者对请求的参数进行处理等。现在过滤器还不能使用,因为SpringBoot
的过滤器依赖其提供的过滤器链,所以要先把自定义的过滤器注册到过滤器链中。
java 代码解读复制代码@Configuration
public class WebConfig {
private final MyFilter myFilter;
@Autowired
public WebConfig(MyFilter myFilter){
this.myFilter = myFilter;
}
@Bean
public FilterRegistrationBean<MyFilter> registFilter(){
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(myFilter);
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(1);
return registrationBean;
}
}
上面的配置类中,我们将MyFilter
对象注册到FilterRegistrationBean
中,可以理解为通过FilterRegistrationBean
将自定义的过滤器注册到了过滤器链中。setOrder(int)
的作用是设置过滤器在过滤器链中的执行顺序,直越小顺序越靠前。addUrlPatterns(String... param)
方法用来设置要拦截的请求路径。现在请求项目中的任意接口,都会在请求前打印hello
,在请求后打印world
。
拦截器的实现
拦截器是基于SpringBoot
中dispatcherServlet
的处理器Handler
拦截器实现的,直接上代码
java 代码解读复制代码@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("请求前执行");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("返回ModelAndView前执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("处理完请求后执行");
}
}
上面是自定义的拦截器,重写了处理器拦截器接口的preHandle
、postHandle
、afterCompletion
方法,分别表示请求前执行、返回ModelAndView前执行、处理完请求后执行,然后同样的需要将拦截器注册到拦截器链中,代码如下
java 代码解读复制代码@Configuration
public class WebConfig implements WebMvcConfigurer {
private final MyFilter myFilter;
private final MyInterceptor myInterceptor;
@Autowired
public WebConfig(MyFilter myFilter,
MyInterceptor myInterceptor){
this.myFilter = myFilter;
this.myInterceptor = myInterceptor;
}
@Bean
public FilterRegistrationBean<MyFilter> registFilter(){
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(myFilter);
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(1);
return registrationBean;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor).
addPathPatterns("/**").order(1);
}
}
直接沿用了自定义过滤器的配置类,并实现SpringMvc
的配置接口WebMvcConfigurer
,重写addInterceptors()
方法,将我们自定义的拦截器注册到拦截器链中,同样的,也可以使用order()
方法设置拦截器的在链中的执行顺序,值越小则优先级越高。addPathPatterns("/**")
表示拦截任意请求。
测试拦截器和过滤器
编写一个测试Controller
java 代码解读复制代码@RestController
@RequestMapping("/sys")
public class SysUserController {
@GetMapping(value = "/test")
public String test() {
return "200";
}
}
上面的接口路径既符合过滤器的过滤条件,也符合拦截器的拦截条件,使用postman
工具请求测试
控制台打印的数据如下图所示:
可以看到先执行了过滤器的请求前置代码打印了hello
,然后过滤器放行后才进入到servlet
控制层执行了控制器的前置、中置及后置方法,最后当控制层处理完请求后,最后才执行了过滤器中的请求后置代码打印了world
。
总结
- 过滤器是在
servlet
之外执行的,过滤器的代码只会在servlet
外层执行 - 拦截器是基于
servlet
的处理器handler
的,所以拦截器会在控制器处理器这一层执行 - 过滤器和拦截器的主要区别是执行的时机不同,虽然他们的作用相似,但是在实际开发中,要根据具体的场景,在两者中做出更合适的选择