拦截器与过滤器

拦截器

介绍

拦截器,顾名思义,是一个全局的拦截某些请求的东西,是AOP思想的具体实现,在Spring中Controller方法执行之前和之后都可以看到拦截器的身影。以下是一个web请求的执行流程

请求的执行流程

  • 浏览器发送请求到Tomcat服务器
  • 需要静态资源直接去项目目录访问,动态资源才进入后端代码
  • 经过几个过滤器过滤
  • 进入中央处理器寻找对应的Controller中的方法
  • 配置的拦截器会动态拦截控制器(Controller)中方法的执行,在方法执行前后进行操作,如jwt校验鉴权

演示

自定义的拦截器需要实现HandlerInterceptor接口,接口中有三个方法preHandle、postHandle等等,都是默认方法,不必强制实现,所有接口的实现类都会通过继承得到这个方法。源码里默认返回true,意思是直接放行。

1
2
3
4
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}

自定义拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Component
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
System.out.println(request.getHeader("host")); // request:请求对象,这里输出localhost:8081
System.out.println(handler.getClass()); // handler:可以获取方法相关信息,这里输出
//class org.springframework.web.method.HandlerMethod
HandlerMethod handlerMethod = (HandlerMethod) handler;
System.out.println(handlerMethod.getMethod().getName()); // 这里输出test
response.setStatus(999); //response: 响应对象,在preHandle里可以任意修改
return true; //如果为false则拦截方法和postHandle和afterCompletion都不执行
}

//原始方法运行后运行,如果原始方法被拦截,则不执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
response.setStatus(888); // 修改失败,因为Controller方法执行结束后响应对象可能已经部分或完全提交
}

//拦截器最后执行的方法,无论原始方法是否执行。用于清理资源
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
response.setStatus(777); // 修改失败,此时响应已经完成并提交给客户端。
}
}

拦截器方法的执行流程

注册拦截器,配置要拦截的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
@Slf4j
public class WebConfig implements WebMvcConfigurer {

@Autowired
private TestInterceptor interceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册拦截器");
registry.addInterceptor(interceptor).addPathPatterns("/**").order(1);
//如果这里还有Interceptor2,配置了多个拦截器形成了拦截器链,那么执行流程是按照配置的顺序先进后出
//具体来说就是:preHandle1->preHandle2—>拦截方法->postHandle2->posthandle1->afterCompletion2
// ->afterCompletion1
}
}

可以通过.order(1)来控制拦截器的执行优先级,数字越小优先级越高

Controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping("/fuish")
public class TestController {

@Autowired
private Dog dog;

@RequestMapping("/test")
public String test(HttpServletResponse response) {
String sound = dog.bark("wof~wof~wof~wof~wof~wof~wof~");
//response.setStatus(200); 当然控制器内也可以随意修改response对象。
return sound;
}
}

Dog实体类:

1
2
3
4
5
6
7
8
9
10
@Component
public class Dog implements Animal{

@Timer
@Override
public String bark(String sound) {
System.out.println("方法执行: " + sound);
return sound;
}
}

好了,浏览器输入localhost:8081/fuish/test,就可以在控制台查收拦截器的执行过程了。(我自己配置的8081端口)

1
2
3
4
5
6
7
8
preHandle
localhost:8081
class org.springframework.web.method.HandlerMethod
test
方法执行: wof~wof~wof~wof~wof~wof~wof~
class com.spring.Dog
postHandle
afterCompletion

过滤器

过滤器,是在java web中将你传入的request、response提前过滤掉一些信息,或者提前设置一些参数。然后再传入Servlet或Struts2的 action进行业务逻辑处理。比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入Servlet或Struts2的action前统一设置字符集,或者去除掉一些非法字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component 
@WebFilter(urlPatterns = "/*") // 配置拦截路径
public class TestFilter implements Filter {
//只会随着服务器的启动初始化一次
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}

//每次请求都会被调用
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
System.out.println("过滤器拦截: " + request.getRequestURI());
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("要走了也得过滤一道。最终返回的状态码:" + response.getStatus());
}

@Override
public void destroy() {
Filter.super.destroy();
}
}
1
2
3
4
5
6
7
8
9
10
过滤器拦截: /fuish/test
preHandle
localhost:8081
class org.springframework.web.method.HandlerMethod
test
方法执行: wof~wof~wof~wof~wof~wof~wof~
class com.spring.Dog
postHandle
afterCompletion
要走了也得过滤一道。最终返回的状态码:999

可以发现过滤器的执行顺序同样遵循先进后出,过滤器链也是一样的

同样可以通过@Order(1)注解来控制过滤器的执行优先级,数字越小优先级越高

拦截器与过滤器的区别

Filter属于Servlet技术, Interceptor属于SpringMVC技术

  • 自定义过滤器需要实现 javax.servlet.Filter接口,而这个接口是在Servlet规范中定义的,或者说过滤器需要依赖于Servlet容器,因此它只能在Web程序中使用
  • 自定义拦截器需要实现org.springframework.web.servlet.HandlerInterceptor接口,存在于SpringMVC中,由Spring容器来管理,不依赖于Servlet容器,是可以单独使用的,因此不仅可以用在Web程序中,也可以用在Application、Swing等程序中

Filter对所有访问进行增强,Interceptor只对特定请求增强

Filter基于函数回调, Interceptor基于AOP思想


拦截器与过滤器
https://payfish.github.io/2024/07/12/拦截器与过滤器/
作者
fu1sh
发布于
2024年7月12日
许可协议