一、实现原理区别
过滤器和拦截器 底层实现方式大不相同:过滤器
是基于函数回调的,拦截器
则是基于Java的反射机制(动态代理)实现的。
过滤器(Filter):它依赖于servlet容器。在实现上是基于函数回调。《Java过滤器Filter详解》
在我们自定义的过滤器中都会实现一个 doFilter()
方法,这个方法有一个FilterChain
参数,而实际上它是一个回调接口。ApplicationFilterChain
是它的实现类, 这个实现类内部也有一个 doFilter()
方法就是回调方法。
每个过滤器Filter 会先执行自身的 doFilter() 过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest, servletResponse),也就是回调ApplicationFilterChain的doFilter() 方法,以此循环执行实现函数回调。
拦截器(Interceptor):在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在service或者一个方法前,调用一个方法,或者在方法后,调用一个方法,比如动态代理就是拦截器的简单实现。
二、使用范围限制
过滤器Filter:过滤器实现的是 javax.servlet.Filter
接口,而这个接口是在Servlet
规范中定义的,也就是说过滤器Filter
的使用要依赖于Tomcat
等容器,导致它只能在web
程序中使用。
拦截器(Interceptor): 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。在SpringMVC中就是依赖于SpringMVC框架
看看代码实例
我们通过代码来查看触发时机、拦截请求返回,执行顺序等区别
1)第一个过滤器:@Order:表示过滤器执行顺序。通过@Order
控制过滤器的级别,值越小级别越高越先执行。
Filter使用@WebFilter注解,但注解@WebFilter是Servlet3.0的规范,并不是Spring boot提供的.因此Filter 依赖于Servlet。
代码语言:javascript复制package com.demo.springboot2.web.service;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.annotation.WebFilter;
import org.springframework.core.annotation.Order;
import javax.servlet.FilterConfig;
import org.springframework.stereotype.Component;
@Order(1)
@WebFilter(filterName = "DemoFilter1", urlPatterns = "/*")
public class DemoFilter1 implements Filter{
@Override
public void destroy() {
System.out.println("DemoFilter1 is destoried");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
//在DispatcherServlet之前执行
System.out.println("############DemoFilter1 doFilte前############");
filterChain.doFilter(request, response);
//在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
System.out.println("############DemoFilter1 doFilter后r############");
}
@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println("DemoFilter1 is inited");
}
}
第二个过滤器:@Order(2):表示过滤器第2个顺序
代码语言:javascript复制@Order(2)
@WebFilter(filterName = "DemoFilter2", urlPatterns = "/*")
public class DemoFilter2 implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
//在DispatcherServlet之前执行
System.out.println("############DemoFilter2 doFilte前############");
filterChain.doFilter(request, response);
//在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
System.out.println("############DemoFilter2 doFilter后############");
}
}
第一个拦截器:
代码语言:javascript复制package com.demo.springboot2.web.service;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
@Component
public class DemoInterceptor1 implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)throws Exception {
System.out.println("************DemoInterceptor1 preHandle**********");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler, ModelAndView arg3) throws Exception {
System.out.println("************DemoInterceptor1 postHandle **********");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) throws Exception {
System.out.println("************DemoInterceptor1 afterCompletion**********");
}
}
第二个拦截器:
代码语言:javascript复制@Component
public class DemoInterceptor2 implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)throws Exception {
System.out.println("************DemoInterceptor2 preHandle**********");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler, ModelAndView arg3) throws Exception {
System.out.println("************DemoInterceptor2 postHandle **********");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) throws Exception {
System.out.println("************DemoInterceptor2 afterCompletion**********");
}
}
注册拦截器:
代码语言:javascript复制package com.demo.springboot2.web.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
@Autowired
private DemoInterceptor1 demoInterceptor1;
@Autowired
private DemoInterceptor2 demoInterceptor2;
// 这个方法是用来配置静态资源的,比如html,js,css,等等
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
// 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
@Override
public void addInterceptors(InterceptorRegistry registry) {
// addPathPatterns("/**") 表示拦截所有的请求,
// excludePathPatterns("/login", "/register") 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
registry.addInterceptor(demoInterceptor1).addPathPatterns("/**").excludePathPatterns("/login", "/register");
registry.addInterceptor(demoInterceptor2).addPathPatterns("/**").excludePathPatterns("/login", "/register");
System.out.println("************addInterceptors**********");
}
}
controller代码:
代码语言:javascript复制package com.demo.springboot2.web.controller;
import org.springframework.web.bind.annotation.*;
@RestController
public class TestController {
@RequestMapping("/")
@ResponseBody
public String index() {
return "Hello world";
}
@RequestMapping("/test")
@ResponseBody
public String test() {
System.out.println("------------TestController-----------");
return "Hello test";
}
}
请求:http://localhost:9091/test
三、触发时机
滤器Filter是在请求进入web容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。 拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
执行顺序 :Filter 处理 -> Interceptor前置 -> controller -> Interceptor处理中 -> Interceptor 处理后 -Filter 处理后
四、拦截请求的范围不同
求资源:http://localhost:9091/favicon.ico
只有两个过滤器Filter
执行:
这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller
中请求或访问static
目录下的资源请求起作用。
五、执行顺序
http://localhost:9091/test
1、多个过滤器的执行顺序跟定义的先后关系有关。 通过@Order控制过滤器的级别,值越小级别越高越先执行。 2、多个拦截器执行顺序跟注册先后顺序有关。 registry.addInterceptor(demoInterceptor1).addPathPatterns("/**").excludePathPatterns("/login", "/register"); registry.addInterceptor(demoInterceptor2).addPathPatterns("/**").excludePathPatterns("/login", "/register"); 拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行。
3、先声明的拦截器 preHandle()
方法先执行,而postHandle()
方法反而会后执行。postHandle()
方法被调用的顺序跟 preHandle()
居然是相反的。那为什么会这样?
我们要知道controller
中所有的请求都要经过核心组件DispatcherServlet
路由,都会执行它的 doDispatch()
方法,而拦截器postHandle()
、preHandle()
方法便是在其中调用的。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
...........
try {
// 获取可以执行当前Handler的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" getRequestUri(request) "] is: " lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 注意: 执行Interceptor中PreHandle()方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 注意:执行Handle【包括我们的业务逻辑,当抛出异常时会被Try、catch到】
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 注意:执行Interceptor中PostHandle 方法【抛出异常时无法执行】
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
}
...........
}
看看两个方法applyPreHandle()
、applyPostHandle()
具体是如何被调用的,就明白为什么postHandle()
、preHandle()
执行顺序是相反的了。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if(!ObjectUtils.isEmpty(interceptors)) {
for(int i = 0; i < interceptors.length; this.interceptorIndex = i ) {
HandlerInterceptor interceptor = interceptors[i];
if(!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
return true;
}
代码语言:javascript复制void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if(!ObjectUtils.isEmpty(interceptors)) {
for(int i = interceptors.length - 1; i >= 0; --i) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
发现两个方法中在调用拦截器数组 HandlerInterceptor[]
时,循环的顺序竟然是相反的。。。,导致postHandle()
、preHandle()
方法执行的顺序相反。