简介
说明
本文介绍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请求与响应,拿不到请求的控制器和请求控制器中的方法的信息。 可以修改请求、响应、参数:比如:修改字符编码、删除低俗文字、删除危险字符、修改参数 | 可以拿到你请求的控制器和方法,却拿不到请求与响应。 |
顺序 | 可指定顺序。 | 可指定顺序。 |
实现方式 | 回调函数(同步调用) | AOP |
使用范围 | 只能用于Web。 (过滤器实现 javax.servlet.Filter 接口(属于Servlet),所以过滤器要依赖于Tomcat等容器,只能在web程序中使用。) | 可用于Web、Application、Swing。 (由Spring提供,不依赖Tomcat等容器,可单独使用。) |
作用范围 | 所有请求。 | 只能处理controller请求。 无法控制静态资源。 |
灵活性/粒度 | 灵活性差(粒度大)。 不能使用 Spring 容器资源。 | 灵活性好(粒度小)。 能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可。 |
打断链路 | 打断方法:请求和响应引发中断,需要额外动作,比如重定向到错误页面。 | 打断方法:preHandle方法内返回 false |
执行次数 | 一个controller周期只调用一次:一个过滤器实例只能在容器初始化时调用一次。 | 一个controller周期可调用多次 |
过滤器
见: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'
请先
!