什么是 Interceptor?
SpringMVC的拦截器HandlerInterceptor,针对调用Controller的请求,进行拦截和处理。
- 我们首先自定义一个Interceptor
public class DemoInterceptor implements HandlerInterceptor {
// 在进入控制器之前执行
// 如果返回值为false,阻止进入控制器
// 用途:拦截未登录请求;控制代码
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
//arg2是准备调用的Controller方法
System.out.println("arg2=" + arg2);
return true;
}
// 控制器执行完成,进入到jsp之前执行
// 用途:日志记录;敏感词语过滤
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
//arg3.getViewName()可获得准备跳转的视图逻辑名 View
System.out.println("往" + arg3.getViewName() + "跳转");
//arg3.getModel()可获得视图数据 Model
System.out.println("data1的值" + arg3.getModel().get("data1"));
String word = arg3.getModel().get("data1").toString();
String newWord = word.replace("色情", "**");
arg3.getModel().put("data1", newWord);
// arg3.getModel().put("data1", "修改后的内容");
}
// jsp执行完成后执行或Controller方法出现异常时执行
// 用途:记录执行过程中出现的异常
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
// arg3是Controller方法中出现的异常
System.out.println("arg3:" + arg3);
if (arg3 != null) {
//若是除零导致的,则arg3.getMessage()显示:/ by zero
System.out.println( arg3.getMessage() );
}
}
}
我们从SpringMVC运行流程中也可以看出拦截器的生效时机:(留意中文注释即可)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
// Expose current LocaleResolver and request as LocaleContext.
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);
// Expose current RequestAttributes to current thread.
RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
if (logger.isDebugEnabled()) {
logger.debug("Bound request context to thread: " + request);
}
try {
ModelAndView mv = null;
try {
processedRequest = checkMultipart(request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Apply preHandle methods of registered interceptors.
// 调用拦截器中 preHandle方法
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// Actually invoke the handler.
// 适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// ——————————— HandlerAdapter调用 Controller方法 ————————————
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// Apply postHandle methods of registered interceptors.
// 调用拦截器的 postHandle方法
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" +
getServletName() + "': assuming HandlerAdapter completed request handling");
}
}
// Trigger after-completion for successful outcome.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
finally {
// Clean up any resources used by a multipart request.
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
// Reset thread-bound context.
RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);
LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);
// Clear request attributes.
requestAttributes.requestCompleted();
if (logger.isDebugEnabled()) {
logger.debug("Cleared thread-bound request context: " + request);
}
}
}
springmvc.xml文件配置
<!-- 拦截器 -->
<mvc:interceptors>
<!--拦截所有控制器 <bean class="com.ljm.interceptor.DemoInterceptor"></bean> -->
<!-- 拦截特定的的url -->
<mvc:interceptor>
<mvc:mapping path="/demo" />
<mvc:mapping path="/demo1" />
<mvc:mapping path="/demo2" />
<bean class="com.bjsxt.interceptor.DemoInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
拦截器栈
当多个拦截器同时生效时,将组成拦截器栈;执行顺序和Interceptor在 springmvc.xml 中的配置顺序有关。例:先配置拦截器 A 在配置拦截器 B 执行顺序为
preHandle(A) --> preHandle(B) --> 控制器方法 --> postHandle(B) --> postHanle(A) --> JSP --> afterCompletion(B) --> afterCompletion(A)
(先进后出)