第15章 拦截器
学习目标
● 了解拦截器定义和配置方式
● 熟悉拦截器的执行流程
● 掌握拦截器的使用
15.1 拦截器概述
Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并做相应的处理。
用途:权限验证、记录请求信息的日志、判断用户是否登录等。
15.1.1 拦截器的定义
1.实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)
2.实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。
public class CustomInterceptor implements HandlerInterceptor{
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
return false;
}
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
preHandler()方法:该方法会在控制器方法前执行,其返回值表示是否中断后续操作。
postHandle()方法:该方法会在控制器方法调用之后,且解析视图之前执行。用途:对请求域中的模型和视图做出进一步的修改。
afterCompletion()方法:该方法会在整个请求完成,即视图渲染结束之后执行。用途:资源清理、记录日志信息
15.1.2 拦截器的配置
<! -- 配置拦截器 -->
<mvc:interceptors>
<! --使用bean直接定义在<mvc:interceptors>下面的Interceptor将拦截所有请求-->
<bean class="com.itheima.interceptor.CustomInterceptor"/>
<! -- 拦截器1-->
<mvc:interceptor>
<! -- 配置拦截器作用的路径 -->
<mvc:mapping path="/**"/>
<! -- 配置不需要拦截器作用的路径 -->
<mvc:exclude-mapping path=""/>
<! -- 定义在<mvc:interceptor>下面的,表示对匹配路径的请求才进行拦截-->
<bean class="com.itheima.interceptor.Interceptor1" />
</mvc:interceptor>
<! -- 拦截器2-->
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean class="com.itheima.interceptor.Interceptor2" />
</mvc:interceptor>
...
</mvc:interceptors>
<mvc:interceptors>元素用于配置一组拦截器,其子元素<bean>中定义的是全局拦截器,它会拦截所有的请求;而<mvc:interceptor>元素中定义的是指定路径的拦截器,它会对指定路径下的请求生效。
<mvc:interceptor>中的子元素必须按照上述代码的配置顺序进行编写,即<mvc:mapping …/>→<mvc:exclude-mapping … />→<bean … />的顺序
15.2 拦截器的执行流程
15.2.1 单个拦截器的执行流程
1.jar包
2.web.xml配置
3.controller层
4.拦截器类,实现HandlerInterceptor接口并且重写方法
5.springmvc-config.xml
<!-- 配置拦截器-->
<mvc:interceptors>
<bean class="com.itheima.interceptor.CustomInterceptor"/>
</mvc:interceptors>
6.success.jsp
7.启动服务器
问题:实现了HandlerInterceptor没有提示重写方法
原因:HandlerInterceptor中的方法是default修饰的
Java 8中引入了一个新的概念,叫做default方法,也可以称为Defender方法,或者虚拟扩展方法(Virtual extension methods)。
Default方法是指,在接口内部包含了一些默认的方法实现(也就是接口中可以包含方法体,这打破了Java之前版本对接口的语法限制),从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。
那怎么重写preHandle()方法呢?
Ctrl+O组合键
本机 alt+shift+T
简单界面出现404,检查requestMapping的value值写对没,检查controller层注解
15.2.2 多个拦截器的执行流程
preHandle()方法:顺序
postHandle()方法和afterCompletion()方法:反序
1.写两个interceptor方法
2.springmvc-config.xml配置
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.itheima.interceptor.Interceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/hellointercpetor"/>
<bean class="com.itheima.interceptor.Interceptor2"/>
</mvc:interceptor>
15.3 应用案例——实现用户登录权限验证
执行流程
1.User类
2.UserController
@Controller
public class UserController {
/*
向用户登录页面跳转
*/
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String toLogin(){
return "login";
}
/*
用户登录
*/
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(User user, Model model, HttpSession session){
// 获取用户名和密码
String username = user.getUsername();
String password = user.getPassword();
//此处模拟从数据库中获取用户名和密码后进行判断
if(username != null && password.equals("123456")){
// 将用户对象添加到Session
session.setAttribute("USER_SESSION",user);
return "redirect:main";
}
model.addAttribute("msg","用户名或密码错误,请重新登陆!");
return "login";
}
/*
向用户主页面跳转
*/
@RequestMapping(value = "/main")
public String toMain(){
return "main";
}
/*
退出登录
*/
@RequestMapping("/logout")
public String logout(HttpSession session){
// 清除Session
session.invalidate();
// 重定向到登录页面的跳转方法
return "redirect:login";
}
}
跳转到登录页面接收的是GET方式提交的方法,而用户登录接收的是POST方式提交的方法
3.拦截器类LoginInterceptor
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("CustomInterceptor preHandle");
// 获取请求的URL
String url = request.getRequestURI();
// URL:除了login.jsp是可公开访问的,其他的URL都进行拦截控制
if(url.indexOf("/login")>=0){
return true;
}
// 获取Session
HttpSession session = request.getSession();
User user = (User) session.getAttribute("USER_SESSION");
// 判断Session中是否有用户数据 如果有则返回true继续向下执行
if(user != null){
return true;
}
request.setAttribute("msg","您还没有登录,请先登录");
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("CustomInterceptor postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("CustomInterceptor afterCompletion");
}
}
4.springmvc-config.xml
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.itheima.interceptor.LoginInterceptor"/>
</mvc:interceptor>
5.main.jsp
<head>
<title>系统主页</title>
</head>
<body>
当前用户:${USER_SESSION.username}
<a href="${pageContext.request.contextPath }/logout">退出</a>
</body>
6.login.jsp
<head>
<title>用户登录</title>
</head>
<body>
${msg}
<form action="${pageContext.request.contextPath }/login" method="post">
用户名:<input type="text" name="username"><br/>
密 码:<input type="password" name="password"><br/>
<input type="submit" value="登录">
</form>
</body>
7.启动服务器
URI是什么?
String、StringBuffer和StringBuilder的区别?
Session相关 getAttribute方法
pageContext.request.contextPath是什么?
什么时候需要用重定向?
【思考题】
1.请简述Spring MVC拦截器的定义方式。
在springmvc-config里添加<mvc:interceptors>标签,定义全局拦截器
<mvc:interceptor>标签配置自定义拦截器。
配置顺序必须是mapping exclude bean 标签
<mvc:inteceptors>
……
<mvc:inteceptor>
……
</mvc:intecptor>
</mvc:inteceptors>
2.请简述单个拦截器和多个拦截器的执行流程。
单个拦截器
prehandler
handle
posthandler
dispatcherservlet
afterhander
多个拦截器
按照配置顺序,prehandeler顺序执行,posthandler和afterhandler逆序执行