过滤器和拦截器
Filter(过滤器) 过滤器的配置比较简单,直接实现Filter
接口即可,也可以通过@WebFilter
注解实现对特定URL拦截。
可以看到Filter 接口中定义了三个方法。
init()
:该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。 注意:这个方法必须执行成功,否则过滤器会不起作用。
doFilter()
:容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。
destroy()
: 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import javax.servlet.*;@Component public class MyFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { System.out.println("MyFilter init" ); } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("MyFilter doFilter" ); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy () { System.out.println("MyFilter destroy" ); } }
Interceptor(拦截器) 拦截器是链式调用 ,一个应用中可以同时存在多个拦截器Interceptor
, 一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。
请求的拦截是通过HandlerInterceptor
来实现,看到HandlerInterceptor
接口中也定义了三个方法。
preHandle()
:这个方法将在请求处理之前进行调用。 注意:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
postHandle()
:只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。 有意思的是:postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle() 方法反而会后执行。
afterCompletion()
:只有在 preHandle() 方法返回值为true 时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import org.springframework.web.servlet.HandlerInterceptor;@Component public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("Interceptor preHandle" ); return true ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("Interceptor postHandle" ); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("Interceptor afterCompletion" ); } }
配置自定义拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 @RequiredArgsConstructor @Configuration public class WebConfig implements WebMvcConfigurer { private final TokenInterceptor tokenInterceptor; @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(tokenInterceptor) .addPathPatterns("/user/**" ) .excludePathPatterns("/user/login" , "/user/register" ); } }
运行顺序:
1 2 3 4 5 6 7 8 9 MyFilter init ... MyFilter doFilter Interceptor preHandle Controller Interceptor postHandle Interceptor afterCompletion ... MyFilter destroy
执行顺序 过滤器用@Order
注解控制执行顺序,通过@Order
控制过滤器的级别,值越小级别越高越先执行。
1 2 3 @Order(Ordered.HIGHEST_PRECEDENCE) @Component public class MyFilter implements Filter {
拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order(int)
手动设置控制,值越小越先执行。
1 2 3 4 5 6 @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor2 ()).addPathPatterns("/**" ).order(2 ); registry.addInterceptor(new MyInterceptor1 ()).addPathPatterns("/**" ).order(1 ); registry.addInterceptor(new MyInterceptor ()).addPathPatterns("/**" ).order(3 ); }
先声明的拦截器 preHandle()
方法先执行,而 postHandle()
方法反而会后执行。
另外还有一点就是 过滤器 不能够使用 Spring 容器资源,只能在 Servlet 容器(e.g. tomcat)启动时调用一次, 而 拦截器 是 Spring 提供的组件,由 Spring 来管理,因此它能使用 Spring 里的任何资源、对象,例如 Service 对象、数据源、事务管理等等, 通过 IoC 注入到 拦截器 中即可。 相比较而言,拦截器 要更灵活一些。