写点什么

【源码系列】Spring 过滤器和拦截器

用户头像
Alex🐒
关注
发布于: 3 小时前

过滤器(Filter)

过滤器有三个基础方法:initdoFilterdestroyinit 过滤器 Bean 实例化时调用。doFilter 在 Action 请求到达时进入,可以实现过滤逻辑,通过 FilterChain 可以继续处理请求。destroy 在 Bean 销毁时调用。


拦截器是基于函数回调,由 Servlet 容器实现的。可以对几乎所有请求进行过滤。过滤器的执行在拦截器之前,不能访问 Action 上下文信息。


使用过滤器可以完成一些过滤操作,对传入的 request、response 过滤或者设置一些参数,然后再传入 Servlet 或者 Controller 进行业务逻辑操作。

使用

实现过滤器

实现 javax.servlet.Filter 接口,添加 @WebFilter 注解,设置过滤器生效路径以及一些参数

@Order 注解指定过滤器应用顺序(数值小的先执行)

@Component@Order(1)@WebFilter(urlPatterns = "/**")public class FilterX implements Filter {
@Override public void init(final FilterConfig filterConfig) throws ServletException { // Filter Bean 实例化时调用 }
@Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { // 进入 Filter 方法,实现 Filter 逻辑 // 通过 FilterChain 继续处理请求,多个 Filter 链式调用 chain.doFilter(request, response); // 请求处理结束后,退出 Filter 方法 }
@Override public void destroy() { // Filter 销毁时调用 }}
复制代码

拦截器(Interceptor)

拦截器有三个基础方法:preHandlepostHandleafterCompletionpreHandle 在进入 Action 处理前调用,如果返回 true,继续处理,返回 false 则中断。postHandle 在 Action 处理完成后调用,如果 Action 抛出异常则不会调用。afterCompletionpostHandle 完成后或 Action 抛出异常时调用。


拦截器是基于 Java 的反射机制,由 Web 框架实现的,不依赖 Servlet 容器,对 Action 请求的生命周期内会多次调用,可以访问 Action 的上下文信息。只能对 Controller 请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。

应用场景

拦截器本质上是面向切面编程(AOP),符合横切关注点的功能都可以放在拦截器中来实现,主要的应用场景包括:

  • 登录验证,判断用户是否登录。

  • 权限验证,判断用户是否有权限访问资源。

  • 后端埋点,统计 PV/UV,监控处理时长。

  • 日志记录,记录请求操作日志,审计。

  • 处理 Cookie、本地化、国际化、主题等。

使用

实现拦截器

实现 org.springframework.web.servlet.HandlerInterceptor 接口

通常会实现 org.springframework.web.servlet.handler.HandlerInterceptorAdapter 接口

preHandle() 方法返回 true,请求会继续处理。


如果使用 Feign,拦截 Feign 调用可以实现 feign.RequestInterceptor

如果使用 RestTemplate,拦截 RestTemplate 请求可以实现 org.springframework.http.client.ClientHttpRequestInterceptor


@Componentpublic class InterceptorX implements HandlerInterceptor {
@Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception { // return true,继续处理请求 return true; }
@Override public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final ModelAndView modelAndView) throws Exception { // 请求处理完成后进入 // 请求抛出异常不会进入 }
@Override public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) throws Exception { // 请求处理完成,并且所有拦截器的 postHandle 处理完成后进入 // 或者请求处理异常后进入 }}
复制代码

注册拦截器

实现拦截器后需要将拦截器注册到容器中,可以通过实现 WebMvcConfigurer,重写 addInterceptors(InterceptorRegistry registry) 方法。

@Configurationpublic class Application implements WebMvcConfigurer {
@Autowired public InterceptorX interceptor;
@Override public void addInterceptors(InterceptorRegistry registry) { // 先 add 的拦截器先生效,链式调用 registry.addInterceptor(interceptor).addPathPatterns("/**"); registry.addInterceptor(new InterceptorX()).addPathPatterns("/**"); }}
复制代码

源代码

过滤器相关代码

使用职责链设计模式

class StandardWrapperValve extends ValveBase {
public final void invoke(Request request, Response response) { // ...
// 创建 FilterChain ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// 进入 Filter 处理逻辑 filterChain.doFilter(request.getRequest(), response.getResponse()); }}
class ApplicationFilterChain implements FilterChain { private int pos = 0; private ApplicationFilterConfig[] filters; public void doFilter(ServletRequest request, ServletResponse response) { internalDoFilter(request,response); } private void internalDoFilter(ServletRequest request, ServletResponse response) { // ... ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = filterConfig.getFilter(); filter.doFilter(request, response, this); // ... }}
复制代码


FilterChain 的创建

class ApplicationFilterFactory {  public static ApplicationFilterChain createFilterChain(ServletRequest request,                                                         Wrapper wrapper, Servlet servlet) {
ApplicationFilterChain filterChain = null; filterChain = new ApplicationFilterChain(); Request req = (Request) request; req.setFilterChain(filterChain); // 这里 set servlet,后面会用到 filterChain.setServlet(servlet); filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// 获取 FilterMap,找到提前注册好的 Filter StandardContext context = (StandardContext) wrapper.getParent(); FilterMap filterMaps[] = context.findFilterMaps();
// 把跟本次请求相关的 Filter 加入 FilterChain // URL 匹配,Servlet 匹配 for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) continue; if (!matchFiltersURL(filterMaps[i], requestPath)) continue; // 源码这里进行了两次 for 循环 // if (!matchFiltersServlet(filterMaps[i], servletName)) // continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); // 加入 ApplicationFilterConfig[] filters filterChain.addFilter(filterConfig); } return filterChain; }}
复制代码

拦截器相关代码

紧跟着过滤器的处理之后

class ApplicationFilterChain implements FilterChain {  // createFilterChain 时设置的  private Servlet servlet = null;    private void internalDoFilter(ServletRequest request,                                ServletResponse response) {    // ...    filter.doFilter(request, response, this);        // 进入 Servlet 处理请求    servlet.service(request, response);      } }
// 会进入到这里class DispatcherServlet extends FrameworkServlet { protected void doDispatch(HttpServletRequest request, HttpServletResponse response) { // ... // 获取到 HandlerExecutionChain HandlerExecutionChain mappedHandler = null; mappedHandler = getHandler(processedRequest); try { // 执行 preHandle,返回 false 退出 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }
//... 请求处理,这里不详细介绍 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 执行 posthandle mappedHandler.applyPostHandle(processedRequest, response, mv); // 这里也会调用 triggerAfterCompletion processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Throwable t) { // 异常会调用 triggerAfterCompletion triggerAfterCompletion(processedRequest, response, mappedHandler, t); } }}
复制代码


HandlerExecutionChain

以 applyPreHandle 举例,获取到 HandlerInterceptor 数组,顺序执行

class HandlerExecutionChain {  boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {    HandlerInterceptor[] interceptors = getInterceptors();
for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { // 触发 afterCompletion triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } return true; }}
复制代码

处理流程图

总结

拦截器和过滤器的区别:

  1. 拦截器是基于 Java 的反射机制的,而过滤器是基于函数回调。

  2. 拦截器不依赖与 servlet 容器,过滤器依赖 servlet 容器。

  3. 拦截器只能对 action 请求起作用,而过滤器则可以对几乎所有的请求起作用。

  4. 拦截器可以访问 action 上下文、值栈里的对象,而过滤器不能访问。

  5. 在 action 的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。

  6. 拦截器基本可以实现过滤器的功能。


执行顺序 :

假设有 A、B 两个 Filter,C、D 两个 Interceptor,一次请求的处理顺序是:

进入 FilterA 代码 -> 进入 FilterB 代码 ->执行 InterceptorC#preHandle -> 执行 InterceptorD#preHandle ->处理 Action(进入 Controller) -> 执行 InterceptorD#postHandle -> 执行 InterceptorC#postHandle ->执行 InterceptorD#afterCompletion -> 执行 InterceptorC#afterCompletion -> 退出 FilterB 代码 -> 退出 FilterA 代码
复制代码


发布于: 3 小时前阅读数: 7
用户头像

Alex🐒

关注

还未添加个人签名 2020.04.30 加入

还未添加个人简介

评论

发布
暂无评论
【源码系列】Spring 过滤器和拦截器