SpringMVC拦截器
简介
拦截器和过滤器不一样,拦截器是作用于控制器方法前后的
拦截器的配置
SpringMVC中的拦截器需要创建一个类实现HandlerInterceptor接口,并重写他的三个抽象方法
- preHandle:作用于控制器方法之前
- postHandle:作用于控制器方法执行完毕后
- afterCompletion:作用于视图渲染等一系列操作之后(可以理解为即将要返回给客户端时)
@Component
public class FirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("FirstInterceptor---->preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("FirstInterceptor---->postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("FirstInterceptor---->afterCompletion");
}
}
SpringMVC配置器必须在spring MVC.xml文件中进行拦截器的配置
<!--配置拦截器-->
<mvc:interceptors>
<!--<bean class="com.yellowstar.mvc.interceptors.FirstInterceptor"></bean>-->
<!--<ref bean="firstInterceptor"></ref>-->
以上通过bean或者ref进行对拦截器的配置,将作用于工程中的所有控制器前后
以下通过mvc:interceptor进行配置
可以通过mvc:mapping设置起作用的请求路径,所有请求用“/**”表示
通过mvc:exclude-mapping设置不起作用的路径
<!-- <mvc:interceptor>-->
<!-- <mvc:mapping path="/**"/>-->
<!-- <mvc:exclude-mapping path="/"/>-->
<!-- <bean class="com.yellowstar.mvc.interceptors.FirstInterceptor"></bean>-->
<!-- </mvc:interceptor>-->
</mvc:interceptors>
多个拦截器的执行顺序
拦截器的执行顺序取决于在xml文件中配置的先后顺序,进行如下配置,来看一下结果
<mvc:interceptors>
<ref bean="firstInterceptor"></ref>
<ref bean="secondInterceptor"></ref>
</mvc:interceptors>
以下是多个拦截器全部放行的例子
通过观察可以发现,在执行preHandle时,写在前面的拦截器会先执行
FirstInterceptor---->preHandle
SecondInterceptor---->preHandle
在执行postHandle、afterCompletion时,写在后面的拦截器会先执行
SecondInterceptor---->postHandle
FirstInterceptor---->postHandle
SecondInterceptor---->afterCompletion
FirstInterceptor---->afterCompletion
看一下源码
通过观察dispatchServlet的doDispatch可以发现,preHandle是在控制器方法执行之前执行的,而mappedHandler是一个集合,集合中包含控制器对象,一个拦截器的集合,而SpringMVC其实是自带一个拦截器,都是放在集合的末尾,最后还有一个下标
看一下applyPreHandle方法,他是一个正序的循环方式,所以最先执行的应该是First过滤器,并且这里要注意的是interceptorIndex,他会在方法的最后执行interceptorIndex=i的操作
如果所有拦截器都放行,程序才会开始执行controller方法,执行完毕后执行applyPostHandle,不难看出applyPostHandle执行的先后顺序是倒序,所以second拦截器会最先执行
最后才会执行triggerAfterCompletion拦截器,这里与前两个不一样的是,for循环的起点是interceptorIndex的值,也是倒叙的形式
第二种情况:多个拦截器中有一个返回值为false,执行结果如下
FirstInterceptor---->preHandle
SecondInterceptor---->preHandle
FirstInterceptor---->afterCompletion
总结
当有多个拦截器且多个拦截器都放行时,在执行preHandle时,写在前面的拦截器会先执行,在执行postHandle、afterCompletion时,写在后面的拦截器会先执行
当有多个拦截器但某个拦截器被拦截时,在执行preHandle时,会执行到被拦截的拦截器,但并不会执行postHandle,在执行afterCompletion时,会从被拦截的拦截器前一个拦截器开始,倒序执行
SpringMVC异常处理
controller控制器中出现异常时,SpringMVC会对其进行异常处理,用来返回一个比较友好的页面
SpringMVC有提供默认的异常处理器,如果我们需要返回一个较为友好的页面,需要配置异常处理器
基于配置文件进行异常处理
在SpringMVC.xml配置文件中配置异常处理
<!--配置异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--配置异常信息-->
<property name="exceptionMappings">
<props>
<!--
key:可能出现异常的全类名
值:出现异常返回的视图名称
-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--出现异常,将异常值放入请求域中,给值配健-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
基于注解方式进行异常处理
@ControllerAdvice
public class ReplaceExceptionController {
//此注解的值为异常类的数组,可以添加多个可能出现的异常
@ExceptionHandler({ArithmeticException.class,NullPointerException.class})
//其中形参的Exception的值为抛出的异常,相当于上述的<prop key="java.lang.ArithmeticException">error</prop>
public String toErrorPage(Exception ex, Model model){
model.addAttribute("ex",ex);
return "error";
}
}