实现拦截器步骤
步骤一 声明拦截器类(实现 handlerInterceptor接口)
1.主要看prehandle方法的返回值确定是否放行,如果是true,表示放行,会执行该拦截器的prehandle方法,
如果有多个拦截器,且全部放行 则preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行
2.如果是false表示不放行。只执行该拦截器的prehandle方法
如果有多个拦截器,某一个不放行则所有拦截器的posthandle一个都不执行,aftercomplitiation倒序执行
举例:有四个拦截器 ,第三个拦截之后没有放行, 就是执行第一个拦截器的prehandle,第二个拦截器的prehandle,第三个拦截器的prehandle, 第二个拦截器的afterCompletion 第一个拦截器的afterCompletion 所有的postHandle方法都不执行
package com.testfilter;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("prehandle方法执行了");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法执行了");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("afterCompletion方法执行了");
}
}
步骤二 在springmvc.xml中配置
mvc:exclude-mapping设置不拦截的路径
如果访问不拦截的路径,则不会执行拦截器的任何一个方法
注意:拦截器中的路径 ,/**代表拦截所有路径,/*代表只是一层目录
/* 可以拦截
/* 不能拦截
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/index" />
<bean class="com.testfilter.AInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
二.拦截后跳转至指定页面
比如拦截所有不是login的请求,并且查看session中是否有用户信息,没有就跳转到登录界面,有就放行,
注意:跳转一定要跳转至不被拦截的请求路径上,否则会一直循环下去 :
-请求被拦截 ----重定向或者请求转发至页面 -----重定向请求或请求转发请求被拦截--请求被拦截
而且返回值一定是false,如果是true,请求转发或重定向一个页面,拦截器放行一个页面,服务器会不知道该显示哪个页面
package com.example.springbootdemo3.testfilter;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
//1.设置拦截器实现handlerinterceptor接口,写拦截方法
public class firstInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("进入了拦截器");
HttpSession session = request.getSession();
if (session.getAttribute("user") == null) {
System.out.println("拦截器跳转了");
response.sendRedirect("index");
}
return false;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
System.out.println("posthandel执行了");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
System.out.println("afterhandle执行了");
}
}
2、拦截器的三个抽象方法
SpringMVC中的拦截器有三个抽象方法:
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
postHandle:控制器方法执行之后执行postHandle()
afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()
3、多个拦截器的执行顺序
a>若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
在前面的会先执行
assecondinterceptor先于后面的bfirstINterceptor
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.testfilter.AsecondInterceptor"></bean>
<!-- <mvc:exclude-mapping path="/testRequestEntity"/>-->
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.testfilter.BfirstInterceptor"></bean>
preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行
为什么posthandle 和aftercomplation会返序执行呢(如下图)?观察源码
4.错误页面
当拦截器不拦截时(prehandel返回值为true),如果请求发生了错误,则会通过两次拦截器。
因为顺序是这样的:
请求到达拦截器 --拦截器放行--页面发生错误
--请求转发错误的提示页面--拦截器放行
就是显示错误页面的请求会被拦截一次。未避免这种情况, 可以设置不拦截 /error请求
<mvc:exclude-mapping path="/error"/>
错误的提示页面指的是
观察源码:
在控制方法中打断点 可以发现下面方法栈中有disparctureservlet,证明disparctureserlvet是先于return 这句执行的,或者说由disparctureservlet控制这个return返回(是不是不对啊?要问问)
在dispratctureservlet 的 mav 方法前是 prehandle方法 之后是posthandle 在之后渲染视图时执行 aftercomplitation 在这几个方法分别打断点
鼠标移动到prehandle 的 mappedHandler
进入这个方法
这个方法遍历了三个拦截器,如果拦截器都不进行拦截(即所有prehandle方法中返回值都为true),则什么都不做,最后返回true 。 interceptorindex就是 最后一个拦截器的编号 (编号从0开始) 。
继续f8 在执行完mv= 之后 进入posthandle方法,发现这里是将interceptorList 倒过来,减减遍历
继续f8 进入aftercomapplication方法,发现这里也是将interceptorList 倒过来,减减遍历
所以 prehandle方法 posthandle方法是倒序调用的
b>若某个拦截器的preHandle()返回了false
如果有拦截器进行了拦截,则立刻返回false,并且interceptorIndex为 上一个拦截器对应的编号。
再看源码可以发现,posthandle一个都不执行,aftercomplitiation倒序执行
总结:有四个拦截器 ,第三个拦截之后没有放行, 就是执行第一个拦截器的prehandle,第二个拦截器的prehandle,第三个拦截器的prehandle,
第二个拦截器的afterCompletion 第一个拦截器的afterCompletion
所有的postHandle方法都不执行