谈谈spring中的拦截器
在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、预先设置数据以及统计方法的执行效率等等。今天就来详细的谈一下spring中的拦截器。spring中拦截器主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。
一,HandlerInterceptor拦截器
HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址,比MethodInterceptor先执行。实现一个HandlerInterceptor拦截器可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。这两种方法殊途同归,其实HandlerInterceptorAdapter也就是声明了HandlerInterceptor接口中所有方法的默认实现,而我们在继承他之后只需要重写必要的方法。下面就是HandlerInterceptorAdapter的代码,可以看到一个方法只是默认返回true,另外两个是空方法:
1 public abstract class HandlerInterceptorAdapter implements HandlerInterceptor { 2 3 /** 4 * This implementation always returns <code>true</code>. 5 */ 6 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 7 throws Exception { 8 return true; 9 } 10 11 /** 12 * This implementation is empty. 13 */ 14 public void postHandle( 15 HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 16 throws Exception { 17 } 18 19 /** 20 * This implementation is empty. 21 */ 22 public void afterCompletion( 23 HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 24 throws Exception { 25 } 26 27 }
这三个方法都是干什么的,有什么作用,什么时候调用,不同的拦截器之间是怎样的调用顺序呢?这还得参考一下DispatcherServlet的doDispatch方法:
1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 HttpServletRequest processedRequest = request; 3 HandlerExecutionChain mappedHandler = null; 4 boolean multipartRequestParsed = false; 5 6 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 7 8 try { 9 ModelAndView mv = null; 10 Exception dispatchException = null; 11 12 try { 13 processedRequest = checkMultipart(request); 14 multipartRequestParsed = (processedRequest != request); 15 16 // Determine handler for the current request. 17 mappedHandler = getHandler(processedRequest); 18 if (mappedHandler == null || mappedHandler.getHandler() == null) { 19 noHandlerFound(processedRequest, response); 20 return; 21 } 22 23 // Determine handler adapter for the current request. 24 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 25 26 // Process last-modified header, if supported by the handler. 27 String method = request.getMethod(); 28 boolean isGet = "GET".equals(method); 29 if (isGet || "HEAD".equals(method)) { 30 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 31 if (logger.isDebugEnabled()) { 32 logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); 33 } 34 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { 35 return; 36 } 37 } 38 39 if (!mappedHandler.applyPreHandle(processedRequest, response)) { 40 return; 41 } 42 43 // Actually invoke the handler. 44 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 45 46 if (asyncManager.isConcurrentHandlingStarted()) { 47 return; 48 } 49 50 applyDefaultViewName(processedRequest, mv); 51 mappedHandler.applyPostHandle(processedRequest, response, mv); 52 } 53 catch (Exception ex) { 54 dispatchException = ex; 55 } 56 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 57 } 58 catch (Exception ex) { 59 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 60 } 61 catch (Error err) { 62 triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); 63 } 64 finally { 65 if (asyncManager.isConcurrentHandlingStarted()) { 66 // Instead of postHandle and afterCompletion 67 if (mappedHandler != null) { 68 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 69 } 70 } 71 else { 72 // Clean up any resources used by a multipart request. 73 if (multipartRequestParsed) { 74 cleanupMultipart(processedRequest); 75 } 76 } 77 } 78 }
它封装了springMVC处理请求的整个过程。首先根据请求找到对应的HandlerExecutionChain,它包含了处理请求的handler和所有的HandlerInterceptor拦截器;然后在调用hander之前分别调用每个HandlerInterceptor拦截器的preHandle方法,若有一个拦截器返回false,则会调用triggerAfterCompletion方法,并且立即返回不再往下执行;若所有的拦截器全部返回true并且没有出现异常,则调用handler返回ModelAndView对象;再然后分别调用每个拦截器的postHandle方法;最后,即使是之前的步骤抛出了异常,也会执行triggerAfterCompletion方法。关于拦截器的处理到此为止,接下来看看triggerAfterCompletion做了什么
1 void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) 2 throws Exception { 3 4 HandlerInterceptor[] interceptors = getInterceptors(); 5 if (!ObjectUtils.isEmpty(interceptors)) { 6 for (int i = this.interceptorIndex; i >= 0; i--) { 7 HandlerInterceptor interceptor = interceptors[i]; 8 try { 9 interceptor.afterCompletion(request, response, this.handler, ex); 10 } 11 catch (Throwable ex2) { 12 logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); 13 } 14 } 15 } 16 }
triggerAfterCompletion做的事情就是从当前的拦截器开始逆向调用每个拦截器的afterCompletion方法,并且捕获它的异常,也就是说每个拦截器的afterCompletion方法都会调用。
根据以上的代码,分析一下不同拦截器及其方法的执行顺序。假设有5个拦截器编号分别为12345,若一切正常则方法的执行顺序是12345的preHandle,54321的postHandle,54321的afterCompletion。若编号3的拦截器的preHandle方法返回false或者抛出了异常,接下来会执行的是21的afterCompletion方法。这里要注意的地方是,我们在写一个拦截器的时候要谨慎的处理preHandle中的异常,因为这里一旦有异常抛出就不会再受到这个拦截器的控制。12345的preHandle的方法执行过之后,若handler出现了异常或者某个拦截器的postHandle方法出现了异常,则接下来都会执行54321的afterCompletion方法,因为只要12345的preHandle方法执行完,当前拦截器的拦截器就会记录成编号5的拦截器,而afterCompletion总是从当前的拦截器逆向的向前执行。
另外,实现HandlerInterceptor拦截器还有一个方法,就是实现WebRequestInterceptor接口。其实它和刚才的两种方法也是殊途同归,最终还是被spring适配成HandlerInterceptor。有一点不同,它的preHandle方法最终只会返回true。
拦截器的配置
<mvc:interceptors> <!-- 使用 bean 定义一个 Interceptor,直接定义在 mvc:interceptors 下面的 Interceptor 将拦截所有的请求 --> <bean class="com.psm.interceptor.WrongCodeInterceptor"/> <mvc:interceptor> <mvc:mapping path="/login.do"/> <!-- 定义在 mvc:interceptor 下面的 Interceptor,表示对特定的请求进行拦截 --> <bean class="com.psm.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
在 Spring 的XML 配置文件中,咱们可以通过mvc:interceptors
标签声明一系列的拦截器,例如:
1 <mvc:interceptors> 2 <bean class="com.hit.interceptor.ContextInterceptor"/> 3 <bean class="com.hit.interceptor.LoginInterceptor"/> 4 <bean class="com.hit.interceptor.WrongCodeInterceptor"/> 5 </mvc:interceptors>
如上所示,这些拦截器就够成了一个拦截器链,或者称之为拦截器栈。而这些拦截器的执行顺序是按声明的先后顺序执行的,即:先声明的拦截器先执行,后声明的拦截器后执行。在mvc:interceptors
标签下声明interceptor
标签主要有两种方式:
- 直接定义一个 Interceptor 实现类的 bean 对象,使用这种方式声明的 Interceptor 拦截器将会对所有的请求进行拦截;
- 使用
mvc:interceptor
标签进行声明,使用这种方式进行声明的 Interceptor 可以通过mvc:mapping
子标签来定义需要进行拦截的请求路径。
此外,由于拦截器是 AOP 编程思想的典型应用,也就意味着咱们可以“切”到具体的“面”进行某些操作。例如,
<bean id="WrongCodeInterceptor" class="com.hit.interceptor.WrongCodeInterceptor"> <property name="userName" value="user-module"></property> </bean> <bean id="loginInterceptor" class="com.hit.interceptor.LoginInterceptor"> <property name="excludePackages"> <list> <value>com.hit.user.exception</value> <value>com.hit.order.exception</value> </list> </property> </bean> <aop:config> <aop:advisor advice-ref="WrongCodeInterceptor" pointcut="execution(* com.hit.*.demo..*.*(..)) || execution(* com.hit.*.demo..*.*(..)) " /> <aop:advisor advice-ref="loginInterceptor" pointcut="execution(* com.hit.*.demo..*.*(..))" /> </aop:config>
引用博客
http://blog.csdn.net/qq_35246620/article/details/68487904,
http://blog.csdn.net/hongxingxiaonan/article/details/48090075。