Spring MVC【九】拦截器与过滤器

Spring MVC中定义了对请求处理拦截的接口HandlerInterceptor,它是通过DispatcherServlet调用处理器执行链(HandlerExecutionChain)实现对请求方法调用的拦截与处理。Filter是Java Servlet的接口,实现该接口可以对Servlet请求和响应进行拦截与处理。Spring继承该接口,提供了很多常用的Filter实现类。

一、Spring MVC拦截器

HandlerInterceptor接口如下:

public interface HandlerInterceptor {    
    //请求处理方法执行前调用。可以用在登录验证、权限控制、参数验证和编码转换等场景
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
    
    //请求处理方法执行之后调用,如果返回类型是视图模型,则在视图渲染之前执行。在该方法中可以设置特定的模型数据用于显示。
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    //在视图渲染之后执行,该方法中可以编写资源释放或异常记录的代码
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }

}

HandlerInterceptor还有一个子接口AsyncHandlerInterceptor用于异步请求的拦截处理,HandlerInterceptorAdapter抽象类(处理拦截器适配器)继承自AsyncHandlerInterceptor。

DispatcherServlet的doDispatch()处理方法中,会依次调用preHandle()、控制器映射方法、postHandle()和afterCompletion()进行处理。doDispatch()方法中可以看到如下:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;    //所有请求的request
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
	Exception dispatchException = null;

	try {
            //检查是否为文件上传的请求,如果是则返回的是MultipartHttpServletRequest,否则还是原来的request
	    processedRequest = checkMultipart(request);
            //是否为文件上传请求的标志
	    multipartRequestParsed = (processedRequest != request);
            
            //获取该请求的处理器链HandlerExecutionChain 
	    mappedHandler = getHandler(processedRequest);
	    if (mappedHandler == null) {
	        noHandlerFound(processedRequest, response);
		return;
	    }
            
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

	    String method = request.getMethod();
	    boolean isGet = "GET".equals(method);    
            if (isGet || "HEAD".equals(method)) {
	        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
	        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
	            return;
                }
            }
            //1.依次调用所有前置拦截方法,applyPreHandle方法返回false时会终止该请求的处理
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
	        return;    
            }
            //2.调用控制器映射方法
	    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            
	    if (asyncManager.isConcurrentHandlingStarted()) {
	        return;
	    }
            
	    applyDefaultViewName(processedRequest, mv);
            //3.执行所有后置拦截
	    mappedHandler.applyPostHandle(processedRequest, response, mv);
        }catch (Exception ex) {
	    dispatchException = ex;
        }catch (Throwable err) {
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);    
    }catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }catch (Throwable err) {
        triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));
    }finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
	    if (mappedHandler != null) {
		mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
	}else {
	    if (multipartRequestParsed) {
	        cleanupMultipart(processedRequest);
	    }
	}
    }
}

MVC拦截器配置

MVC拦截器有多种配置方式,下面是使用<mvc:interceptors>配置拦截器。

<mvc:interceptors>标签下配置拦截器Bean

<mvc:interceptors>
    <!--自定义的拦截器-->
    <bean class="pers.xy.bs.interceptor.PermissionInterceptor"></bean>
</mvc:interceptors>

这种配置会为每个处理器映射器(HandlerMapping)注入一个拦截器,拦截的是所有请求,包括对静态资源的请求,无论他是否被放行。

请求地址的匹配

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/user/**"/>
	<bean class="pers.xy.bs.interceptor.PermissionInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>

以上配置,PermissionInterceptor会对所有以“/user”开头的请求。

在基于Java代码注解的项目中,定义继承WebMvcConfigurationSupport类的注解配置类,通过覆写addInterceptors()方法进行拦截器的添加以及匹配路径的设置,如下:

@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new PermissionInterceptor()).addPathPatterns("/user/**");
	}
}

二、过滤器

Filter是Servlet 2.3开始提供的接口,其实现对Servlet请求及响应的拦截、过滤和预处理,应用场景包括:登录验证、数据压缩、加密或字符转码。过滤器的处理流程如下:

  1. 过滤器在Servlet处理之前拦截请求;
  2. 在doFilter()方法中对请求进行预处理,决定是否放行。放行后调用chain.doFilter(request,response)继续处理。也可以中断请求,转发或者重定向到其他Servlet或页面进行处理;
  3. Servlet处理请求并返回响应。
  4. 过滤器拦截响应返回(ServletResponse)并可对响应进行处理。
  5. 响应返回给客户端。

Spring MVC提供了一些过滤器供我们使用。

  • GenericFilterBean:Spring 过滤器实现的通用抽象父类,它有如下作用:
    • 实现了Filter接口,具备Filter拦截功能;
    • 实现InitializingBean和DisposableBean接口,具备Bean的afterPropertiesSet()和destory()生命周期回调方法;
    • 实现Aware接口,可以获取Bean的名称,环境对象和ServletContext对象。
  • DelegatingFilterProxy:委派过滤器代理。如果配置了这个Bean,则其会作为所有Filter的代理,由Spring容器管理Filter的生命周期。
  • ResourceUrlEncodingFilter:资源URL编码过滤器,用于将内部资源地址转换为外部地址。
  • OncePerRequestFilter:继承自GenericFilterBean,OncePerRequestFilter的意思是执行一次的过滤器。该类是为了解决Filter可能被执行多次的问题。此外,该类还处理了Servlet版本兼容的问题。在Servlet2.3版本中,Filter会拦截所有请求,但在Servlet2.4及之后的版本中,Filter只会拦截外部请求,对于内部的forward和include的转发请求不会过滤。为了稳妥起见,过滤器实现从该类继承。
  • AbstractRequestLoggingFilter:日志处理过滤器抽象类,包括两个子类过滤器:CommomsRequestLoggingFilter和ServletContextRequestLoggingFilter。
  • CharacterEncodingFilter:字符编码过滤器,可以对POST请求中的中文字符进行转码,解决中文乱码的问题。
  • CorsFilter:跨域过滤器,处理跨域访问配置。
  • ForwardedHeaderFilter:转发请求头过滤器,用于获取和处理Forward请求头中的信息。
  • HiddenHttpMethodFilter:HTML的Form表单仅支持GET和POST类型的方法,不支持PUT、DELETE等请求方法,但HiddenHttpMethodFilter过滤器扩展了此功能。
  • MultipartFilter:文件上传过滤器。

三、过滤器与拦截器的关系

过滤器和拦截器都可以在请求处理方法执行前后进行额外的处理,两者的应用场景类似,但是属于完全不同的组件,主要区别如下:

  • 技术实现:MVC Interceptor是Spring MVC的功能,Filter是Servlet的机制;
  • 适用范围:MVC Interceptor可以用在Web应用和桌面应用中,Filter只能使用在Java Web项目中;
  • 配置方式:MVC Interceptor配置在Spring MVC配置文件中,Filter配置在web.xml中。
  • 管理方式:MVC拦截器由Spring容器管理,可以获取容器中的对象,Filter默认不由容器管理,可以配置容器进行生命周期管理,但是无法获取到容器对象。

MVC拦截器、过滤器及Spring AOP的增强和拦截器可以在项目中同时使用,逻辑结构及处理流程如下图所示:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值