1 拦截器的作用
Spring MVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。用户可以自己定义一些拦截器来实现特定的功能。
谈到拦截器,还要向大家提一个词——拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
说到这里,可能大家脑海中有了一个疑问,这不是我们之前学的过滤器吗?是的,它和过滤器是有几分相似,但是也有区别:
- 过滤器是Servlet规范中的一部分,任何java web工程都可以使用;
- 拦截器是Spring MVC框架自己的,只有使用了Spring MVC框架的工程才能用;
- 过滤器在url-pattern中配置了/*之后,可以对所有要访问的资源拦截;
- 拦截器只会拦截访问的控制器方法,如果访问的是jsp、html、css、image或js则不会进行拦截。它也是AOP思想的具体应用。
2 自定义拦截器的步骤
我们要想自定义拦截器,要求必须实现HandlerInterceptor接口。
2.1 编写一个普通类实现HandlerInterceptor接口
/**
* 自定义拦截器
*/
public class HandlerInterceptorDemo1 implements HandlerInterceptor {
@Override
public Boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle拦截器拦截了");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法执行了");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion方法执行了");
}
}
2.2 配置拦截器
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="handlerInterceptorDemo1" class="com.itheima.web.interceptor.HandlerInterceptorDemo1"></bean>
</mvc:interceptor>
</mvc:interceptors>
2.3 测试运行结果:
3 拦截器的细节
3.1 拦截器的放行
放行:如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器中的方法。
3.2 拦截器中方法的说明
public interface HandlerInterceptor {
default Boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
Boolean preHandle()
:- 如何调用:按拦截器定义顺序调用;
- 何时调用:只要配置了都会调用;
- 有什么用:如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
void preHandle()
:- 如何调用:按拦截器定义逆序调用;
- 何时调用:在拦截器链内所有拦截器返成功调用
- 有什么用:在业务处理器处理完请求后,但是DispatcherServlet向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
void afterCompletion()
:- 如何调用:按拦截器定义逆序调用;
- 何时调用:只有preHandle返回true才调用
- 有什么用:在DispatcherServlet完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
思考:如果有多个拦截器,这时拦截器1的preHandle方法返回true,但是拦截器2的preHandle方法返回false,而此时拦截器1的afterCompletion方法是否执行?
3.3 拦截器的作用路径
作用路径可以通过在配置文件中配置:
<!-- 配置拦截器的作用范围 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 用于指定对拦截的url -->
<mvc:mapping path="/**" />
<!-- 用于指定排除的url-->
<mvc:exclude-mapping path=""/>
<bean id="handlerInterceptorDemo1" class="com.itheima.web.interceptor.HandlerInterceptorDemo1"></bean>
</mvc:interceptor>
</mvc:interceptors>
3.4 多个拦截器的执行顺序
多个拦截器是按照配置的顺序决定的:
4 正常流程测试
4.1 配置文件:
<!-- 配置拦截器的作用范围 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<!-- 用于指定对拦截的url -->
<bean id="handlerInterceptorDemo1" class="com.itheima.web.interceptor.HandlerInterceptorDemo1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean id="handlerInterceptorDemo2" class="com.itheima.web.interceptor.HandlerInterceptorDemo2"></bean>
</mvc:interceptor>
</mvc:interceptors>
4.2 拦截器1的代码:
/**
* 自定义拦截器
* */
public class HandlerInterceptorDemo1 implements HandlerInterceptor {
@Override public Boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器1:preHandle拦截器拦截了");
return true;
}
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器1:postHandle方法执行了");
}
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器1:afterCompletion方法执行了");
}
}
4.3 拦截器2的代码:
/**
* 自定义拦截器
*/
public class HandlerInterceptorDemo2 implements HandlerInterceptor {
@Override
public Boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器2:preHandle拦截器拦截了");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器2:postHandle方法执行了");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器2:afterCompletion方法执行了");
}
}
4.4 运行结果:
5 中断流程测试
5.1 配置文件:
<!-- 配置拦截器的作用范围 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<!-- 用于指定对拦截的url -->
<bean id="handlerInterceptorDemo1" class="com.itheima.web.interceptor.HandlerInterceptorDemo1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean id="handlerInterceptorDemo2" class="com.itheima.web.interceptor.HandlerInterceptorDemo2"></bean>
</mvc:interceptor>
</mvc:interceptors>
5.2 拦截器1的代码:
/**
* 自定义拦截器
*/
public class HandlerInterceptorDemo1 implements HandlerInterceptor {
@Override
public Boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器1:preHandle拦截器拦截了");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器1:postHandle方法执行了");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器1:afterCompletion方法执行了");
}
}
5.3 拦截器2的代码:
/**
* 自定义拦截器
*/
public class HandlerInterceptorDemo2 implements HandlerInterceptor {
@Override
public Boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器2:preHandle拦截器拦截了");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器2:postHandle方法执行了");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器2:afterCompletion方法执行了");
}
}
5.4 运行结果:
6 拦截器的简单案例(验证用户是否登录)
6.1 实现思路
1、有一个登录页面,需要写一个controller访问页面 2、登录页面有一提交表单的动作。需要在controller中处理。2.1、判断用户名密码是否正确 2.2、如果正确 向session中写入用户信息 2.3、返回登录成功。3、拦截用户请求,判断用户是否登录 3.1、如果用户已经登录。放行 3.2、如果用户未登录,跳转到登录页面
6.2 控制器代码
//登陆页面
@RequestMapping("/login")
public String login(Model model)throws Exception{
return "login";
}
//登陆提交
//userid:用户账号,pwd:密码
@RequestMapping("/loginsubmit")
public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
//向session记录用户身份信息
session.setAttribute("activeUser", userid);
return "redirect:/main.jsp";
}
//退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{
//session过期
session.invalidate();
return "redirect:index.jsp";
}
6.3 拦截器代码
public class LoginInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//如果是登录页面则放行
if(request.getRequestURI().indexOf("login.action")>=0){
return true;
}
HttpSession session = request.getSession();
//如果用户已登录也放行
if(session.getAttribute("user")!=null){
return true;
}
//用户没有登录挑战到登录页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}