1. 拦截器概述
SpringMVC 中的拦截器似于 Servlet 中的过滤器,它主要作用是在用户请求到达控制器之前或之后进行拦截,可以对请求或响应进行一些自定义处理。例如通过拦截器可以进行用户登录验证、用户权限验证、记录请求信息的日志、修改响应信息等等。
拦截器将按照一定的顺序联结成一条链,这条链称为拦截链(Interceptor Chain)
。在访问被拦截的方法,或者字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
拦截器就是AOP思想的具体体现。
拦截器与过滤器的区别
过滤器注重在请求和响应的流程中进行处理,可以修改请求和响应内容。例:设置编码格式、请求头、状态码等等操作。
拦截器注重对于控制器进行前置和后置处理,在请求到达控制器之前或之后的特定操作。例:打印日志、权限验证等等操作。
使用范围:
- 过滤器是 servlet 规范的一部分,任何javaweb工程都可以使用
- 拦截器是 springmvc 框架自己的,只有使用了springmvc框架的工程才能使用
拦截范围:
- 过滤器在"url-pattern"中配置"/*"之后可以对所有要访问的资源拦截
- 拦截器只会拦截访问的控制器方法,如果访问的是 jsp\html\css\image或者js等静态资源,拦截器不会进行拦截
fillter、servlet、interceptor、controller执行顺序
2. 拦截器自定义
拦截器中的三个方法:
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法。
postHandle:控制器方法执行之后执行postHandle()。
afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()。
2.1 拦截器创建
拦截器创建有两种方式,分别是通过实现 HandlerInterceptor接口 和实现 WebRequestInterceptor接口 两种方式。
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 { }
}
public interface WebRequestInterceptor {
void preHandle(WebRequest request) throws Exception;
void postHandle(WebRequest request, @Nullable ModelMap model) throws Exception;
void afterCompletion(WebRequest request, @Nullable Exception ex) throws Exception;
}
2.1.1 HandlerInterceptor
通过 HandlerInterceptor 接口创建自定义拦截器,这三个方法可以选择性重写,因为在接口中它的修饰符是 default。
@Slf4j
@Component
public class HandlerInterceptorDemo implements HandlerInterceptor {
/**
* 原始方法调用前执行的内容
* 返回值类型可以拦截控制的执行,true放行,false终止
* @param request 请求信息
* @param response 响应信息
* @param handler 拦截器对象
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//return HandlerInterceptor.super.preHandle(request, response, handler);
log.info("原始方法调用前执行的内容 - request.getContextPath(): {}",request.getContextPath());
log.info("原始方法调用前执行的内容 - request.getMethod(): {}",request.getMethod());
return true;
}
/**
* 原始方法调用后执行的内容
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("原始方法调用后执行的内容 - modelAndView: {}",modelAndView);
//HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 原始方法调用完成后执行的内容
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("原始方法调用完成后执行的内容:");
//HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2.1.2 WebRequestInterceptor
通过 WebRequestInterceptor 接口创建自定义拦截器,这三个方法必须全部重写。
@Slf4j
@Component
public class WebRequestInterceptorDemo implements WebRequestInterceptor {
/**
* 原始方法调用前执行的内容
* @param request
* @throws Exception
*/
@Override
public void preHandle(WebRequest request) throws Exception {
log.info("原始方法调用前执行的内容 - request.getContextPath(): {}",request.getContextPath());
}
/**
* 原始方法调用后执行的内容
* @param request
* @param model
* @throws Exception
*/
@Override
public void postHandle(WebRequest request, ModelMap model) throws Exception {
log.info("原始方法调用后执行的内容 - ModelMap: {}",model);
}
/**
* 原始方法调用完成后执行的内容
* @param request
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(WebRequest request, Exception ex) throws Exception {
log.info("原始方法调用完成后执行的内容:");
}
}
2.2 拦截器配置
2.2.1 XML配置
springmvc.xml配置方式。
<mvc:interceptors>
<!--基本配置 第一种方式: 在拦截器类上不需要添加 @Component注解, 注意基本配置默认拦截所有请求-->
<bean class="com.app.library.interceptor.HandlerInterceptorDemo"></bean>
<!--基本配置 第二种方式: 在拦截器类上添加 @Component注解,并在配置springmvc.xml文件中开启注解驱动-->
<ref bean="webRequestInterceptorDemo"/>
<!--高级配置:可以针对性拦截某些路径,某些路径进行放行-->
<mvc:interceptor>
<!--拦截所有路径-->
<mvc:mapping path="/**"/>
<!--放行 /test/ 路径的处理器 -->
<mvc:exclude-mapping path="/test/"/>
<!--指定使用那个拦截器-->
<ref bean="HandlerInterceptorDemo"/>
</mvc:interceptor>
</mvc:interceptors>
2.2.2 配置类配置
@Configuration 注解是 Spring3.0 之后提供的注解。
@Configuration //标注这是一个配置类
@EnableWebMvc //启动webmvc配置
@ComponentScan("com.app.library.interceptor.controller") //根据路径扫描包下的处理器,并进行过拦截
public class InterceptorConfig implements WebMvcConfigurer { //实现WebMvcConfigurer接口配置拦截器信息可以简化开发,但具有一定的侵入性。
@Autowired
private HandlerInterceptorDemo Interceptor;
//@Autowired
//private WebRequestInterceptorDemo Interceptor;
/** 将拦截器与拦截路径进行绑定 */
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(Interceptor).addPathPatterns("/inter/*");
// "/**" 拦截全部请求
// "/inter/*" 拦截这个请求路径下的所有请求
// "/inter/handle" 拦截指定路径的请求
}
}
3. 拦截器执行流程
3.1 拦截器执行顺序
public class DispatcherServlet extends FrameworkServlet {
//DispatcherServlet 核心方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//1 applyPreHandle方法执行 这里会根据preHandle()的返回值进行判断是否执行 ,ture-执行处理器方法.. false-直接return,不向下走
if(!mappedHandler.applyPreHandle(processedRequest, response)){ return; }
//2 处理器方法执行
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//3 applyPostHandle方法执行
mappedHandler.applyPostHandle(processedRequest, response, mv);
//4 页面渲染 + afterCompletion方法执行
processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
//4.1 页面渲染
render(mv, request, response);
//4.2 triggerAfterCompletion方法执行
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
public class HandlerExecutionChain {
//拦截器集合
private final List<HandlerInterceptor> interceptorList;
//集合角标
private int interceptorIndex;
//1.1 执行 applyPreHandle
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 正序 遍历拦截器List集合
for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
//获取拦截器对象
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
//执行preHandle方法,并判断preHandle方法返回值 preHandle方法返回true-不走下面代码块 返回false-执行代码块
if (!interceptor.preHandle(request, response, this.handler)) {
//当 preHandle() = false 执行 triggerAfterCompletion()
this.triggerAfterCompletion(request, response, (Exception)null);
//返回false
return false;
}
}
return true;
}
//3.1 执行applyPostHandle
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
// 倒序 遍历拦截器List集合
for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
//获取拦截器对象
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
//执行拦截器 postHandle方法
interceptor.postHandle(request, response, this.handler, mv);
}
}
//4.2.1 triggerAfterCompletion方法执行
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
//按照 拦截器集合当时最大脚标 进行 倒序 遍历拦截器集合
for(int i = this.interceptorIndex; i >= 0; --i) {
//获取拦截器对象
HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
try {
//执行 afterCompletion方法
interceptor.afterCompletion(request, response, this.handler, ex);
} catch (Throwable var7) {
logger.error("HandlerInterceptor.afterCompletion threw exception", var7);
}
}
}
}
3.2 多个拦截器的执行顺序
状况一:若每个拦截器的preHandle()都返回true
preHandle按照配置文件中的顺序执行;handle方法全部执行,postHandle按照配置文件中的反序执行;afterCompletion按照配置文件中的反序执行。
状况二:若拦截链中某拦截器的preHandle()返回了false
该拦截器之前的所有preHandle()方法都执行,该拦截器的preHandle()方法也会执行。 handle方法不会执行,所有拦截器的postHandle()方法都不执行,该拦截器之前的afterCompletion()方法,逆序执行。