SpringMVC可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,用户自定义的拦截器必须实现HandlerInterceptor接口。
1、拦截器的定义
自定义拦截器(自定义类)必须实现HandlerInterceptor接口,重写接口的3个方法。
package com.yuanlong.springmvc.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class Interceptor implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,Object arg2) throws Exception { //***执行Handler方法之前调用(应用场景:身份认证、身份授权) //return true表示放行继续往下执行,return false表示拦截不再往下执行 return true; } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { //***进入Handler方法之后,返回modelAndView之前调用 //(应用场景:从modelAndView出发,将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图) } @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { //***Handler方法执行完成后调用(应用场景:统一异常处理,统一日志处理) } }
其中值得说明的是:(1).SpringMVC的自定义拦截器必须实现HandlerInterceptor接口(注意是org.springframework.web.servlet包下的),重写接口的3个方法。(2).关于重写的3个方法的调用时机:
preHandle:调用Handler方法之前调用(preHandle方法返回false表示拦截不再向下执行,返回true表示放行继续往下执行)postHandle:进入Handler方法之后,返回ModelAndView之前调用afterCompletion:执行Handler方法完成之后调用
2、拦截器的配置(在springmvc.xml文件中添加如下配置信息)
SpringMVC拦截器的配置有两种方式:
(1).针对HandlerMapping配置
针对HandlerMapping进行SpringMVC拦截器的配置,只会拦截通过已配置的HandlerMapping映射成功的handler。该方法比较局限,在实际开发中使用较少。
(2).配置类似全局拦截器<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="Interceptor1"/> <ref bean="Interceptor2"/> </list> </property> </bean> <bean id="Interceptor1" class="com.yuanlong.springmvc.interceptor.Interceptor1"/> <bean id="Interceptor2" class="com.yuanlong.springmvc.interceptor.Interceptor2"/>
配置类似全局的拦截器之后,springmvc框架会将配置的类似全局的拦截器注入到每个HandlerMapping中。实际开发中常使用这种配置方式。
<!-- 自定义拦截器的配置 --> <mvc:interceptors> <!-- 多个拦截器,顺序执行 --> <mvc:interceptor> <mvc:mapping path="/**"/><!-- "/**"表示拦截所有的URL(包括子URL路径) --> <bean class="com.yuanlong.springmvc.interceptor.Interceptor1"/> <!-- 自定义拦截器的路径 --> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.yuanlong.springmvc.interceptor.Interceptor2"/> </mvc:interceptor> </mvc:interceptors>
3、多拦截器的测试
我们通过下面这个测试来说明自定义拦截器中三个重写方法的执行顺序问题。目的是让我们在实际开发中清楚自己想要添加的某个拦截操作(如身份校验)应该加在三个方法中的哪个方法中。
问题描述:在springmvc.xml文件中配置两个自定义拦截器,自定义两个拦截器,分别在两个自定义拦截器的三个重写方法中打印一句话,以此来研究执行顺序问题。
3.1、自定义两个拦截器
package com.yuanlong.springmvc.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /** * 自定义拦截器1:SpringMVC的自定义拦截器需实现HandlerInterceptor接口(注意是org.springframework.web.servlet包下的),重写3个方法 * 三个方法的调用时机: * preHandle:调用Handler方法之前调用(preHandle方法返回false表示拦截不再向下执行,返回true表示放行继续往下执行) * postHandle:进入Handler方法之后,返回ModelAndView之前调用 * afterCompletion:执行Handler方法完整之后调用 * */ public class Interceptor1 implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { //***执行Handler方法之前调用 //应用场景:身份认证、身份授权 System.out.println("拦截器1:preHandle方法"); return true; } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { //***进入Handler方法之后,返回modelAndView之前调用 //应用场景:从modelAndView出发,将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图 System.out.println("拦截器1:postHandle方法"); } @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { //***Handler方法执行完成后调用 //应用场景:统一异常处理,统一日志处理 System.out.println("拦截器1:afterCompletion方法"); } }
package com.yuanlong.springmvc.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /** * 自定义拦截器2:SpringMVC的自定义拦截器需实现HandlerInterceptor接口(注意是org.springframework.web.servlet包下的),重写3个方法 * 三个方法的调用时机: * preHandle:调用Handler方法之前调用(preHandle方法返回false表示拦截不再向下执行,返回true表示放行继续往下执行) * postHandle:进入Handler方法之后,返回ModelAndView之前调用 * afterCompletion:执行Handler方法完整之后调用 * */ public class Interceptor2 implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { //***执行Handler方法之前调用 //应用场景:身份认证、身份授权 System.out.println("拦截器2:preHandle方法"); return true; } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { //***进入Handler方法之后,返回modelAndView之前调用 //应用场景:从modelAndView出发,将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图 System.out.println("拦截器2:postHandle方法"); } @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { //***Handler方法执行完成后调用 //应用场景:统一异常处理,统一日志处理 System.out.println("拦截器2:afterCompletion方法"); } }
3.2、配置类似全局拦截器(在springmvc.xml文件中追加如下配置信息)
3.3、编写简单的Handler方法<!-- 自定义拦截器的配置 --> <mvc:interceptors> <!-- 多个拦截器,顺序执行 --> <mvc:interceptor> <mvc:mapping path="/**"/><!-- "/**"表示拦截所有的URL(包括子URL路径) --> <bean class="com.yuanlong.springmvc.interceptor.Interceptor1"/> <!-- 自定义拦截器的路径 --> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.yuanlong.springmvc.interceptor.Interceptor2"/> </mvc:interceptor> </mvc:interceptors>
因为在配置文件中配置的是"/**",表示拦截所有的URL,所以此处也可以不编新的Handler方法,直接访问你项目中已有的任意一个Handler,直接在浏览器地址栏输入"http://localhost:8888/springMVC/interceptorTest.do"访问,注意interceptorTest.do是你要访问的Handler方法的注解@RequestMapping("interceptorTest.do")参数。package com.yuanlong.springmvc.handlers; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class InterceptorControllerTest { @RequestMapping("interceptorTest.do") public ModelAndView interceptorTest(){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("message", "SpringMVC拦截器测试返回页面"); modelAndView.setViewName("/WEB-INF/jsp/success.jsp"); return modelAndView; } }
3.4、分情况分析
上面说过,自定义拦截器中preHandle方法返回boolean的值,返回true表示放行,返回false表示拦截(不再往下执行)。我们就通过改变两个拦截器preHandle方法的返回值来作分析。
a、拦截器1和拦截器2都放行(返回true)
从控制台打印情况结合拦截器的注册顺序,我们不难看出:
在拦截器全部放行的情况下,preHandle方法顺序(配置文件中拦截器的注册顺序)执行,postHandle和afterComletion方法逆序执行。
b、拦截器1放行(true),拦截器2拦截(false)
拦截器1放行,拦截器2 preHandle方法会执行。
拦截器2拦截,拦截器2 postHandle和afterCompletion都不会执行。
只要有一个拦截器拦截,所有拦截器的postHandle方法都不会执行。c、拦截器1拦截(false),拦截器2放行(true)
前面拦截器被拦截,仅会执行被拦截拦截器的prehandle方法,后面拦截器不会执行。
至此,SpringMVC的自定义拦截器基本介绍完毕。关于拦截器的实际应用,读者可以自行测试。
感兴趣的小伙伴可以关注一下博主的公众号,1W+技术人的选择,致力于原创技术干货,包含Redis、RabbitMQ、Kafka、SpringBoot、SpringCloud、ELK等热门技术的学习&资料。