Spring·基础入门篇(二十) SpringMVC 中的拦截器


1. SpringMVC拦截器作用

  1. SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术。
  2. 可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链
    中的拦截器会按着定义的顺序执行。
  3. 拦截器和过滤器的功能比较类似,有区别
  1. 过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术。
  2. 拦截器是SpringMVC框架独有的。
  3. 过滤器配置了/*,可以拦截任何资源。
  4. 拦截器只会对控制器中的方法进行拦截。
  1. 拦截器也是AOP思想的一种实现方式
  2. 想要自定义拦截器,需要实现HandlerInterceptor接口。

2. 自定义SpringMVC拦截器

2.1 编写一个普通类实现 HandlerInterceptor 接口

/**
 * @author: LzCc
 * @blog: 
 * @description: 自定义拦截器
 */
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器执行了...");
        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="/*"/>
        <!-- 哪些方法不进行拦截
        <mvc:exclude-mapping path=""/>
        -->
        <!-- 注册拦截器对象 -->
        <bean class="top.lzchao.interceptor.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

2.3 测试运行结果

springmvc 拦截器注入bean springmvc中拦截器_mvc

3. SpringMVC拦截器的细节

3.1 拦截器的放行

放行的含义是指,如果有下一个拦截器就执行下一个,如果该拦截器处于拦截器链的最后一个,则执行控制器中的方法。

springmvc 拦截器注入bean springmvc中拦截器_springmvc 拦截器注入bean_02

3.2 拦截器中方法的说明

preHandle:

  • 如何调用:
  • 按拦截器定义顺序调用
  • 何时调用:
  • 只要配置了都会调用
  • 有什么用:
  • 如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去
  • 进行处理,则返回 true
  • 如果程序员决定不需要再调用其他的组件去处理请求,则返回 false

postHandle:

  • 如何调用:
  • 按拦截器定义逆序调用
  • 何时调用:
  • 在拦截器链内所有拦截器返成功调用
  • 有什么用:
  • 在业务处理器处理完请求后,但是 DispatcherServlet 向客户端返回响应前被调用
  • 在该方法中对用户请求 request 进行处理

afterCompletion:

  • 如何调用:
  • 按拦截器定义逆序调用
  • 何时调用:
  • 只有 preHandle 返回 true 才调用
  • 有什么用:
  • 在 DispatcherServlet 完全处理完请求后被调用
  • 可以在该方法中进行一些资源清理的操作

3.3 拦截器的作用路径

作用路径可以通过在配置文件中配置

<mvc:mapping path="/**" /><!-- 用于指定对拦截的 url -->
<mvc:exclude-mapping path=""/><!-- 用于指定排除的 url-->

4. 多个拦截器的执行顺序

4.1 配置多个拦截器

拦截器一:

/**
 * @author: LzCc
 * @blog: 
 * @description: 拦截器1
 */
public class MyInterceptor1 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 方法执行了");
    }
}

拦截器二:

/**
 * @author: LzCc
 * @blog: 
 * @description: 拦截器2
 */
public class MyInterceptor2 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 方法执行了");
    }
}

配置多个拦截器:

<!-- 配置拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/*"/>
        <bean class="top.lzchao.interceptor.MyInterceptor1"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/*"/>
        <bean class="top.lzchao.interceptor.MyInterceptor2"/>
    </mvc:interceptor>
</mvc:interceptors>

4.2 测试拦截器正常流程

springmvc 拦截器注入bean springmvc中拦截器_springmvc 拦截器注入bean_03

4.3 测试拦截器中断流程

设置拦截器2不放行

springmvc 拦截器注入bean springmvc中拦截器_SpringMVC_04

4.4 总结多个拦截器的执行顺序

springmvc 拦截器注入bean springmvc中拦截器_SpringMVC_05

5. 拦截器的简单案例

5.1 实现验证用户是否登录思路

  1. 有一个登录页面,需要写一个 controller 访问页面
  2. 登录页面有一提交表单的动作。需要在 controller 中处理。
    2.1. 判断用户名密码是否正确
    2.2. 如果正确 向 session 中写入用户信息
    2.3. 返回登录成功。
  3. 拦截用户请求,判断用户是否登录
    3.1. 如果用户已经登录。放行
    3.2. 如果用户未登录,跳转到登录页面

5.2 控制器代码

//登陆页面
@RequestMapping("/login")
public String login(Model model)throws Exception{
	return "login";
}
//登陆提交
@RequestMapping("/loginsubmit")
public String loginsubmit(HttpSession session, String username, 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";
}

5.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;
	}
}