Interceptor 拦截器学习:
- 1、了解spring mvc拦截器的概念、作用!
- 2、拦截器的实现方式!
- 3、拦截器执行流程 和 拦截器链!
- 4、完成登陆拦截案例!
Spring MVC拦截器 ~~简介
Spring mvc的Interceptor拦截器主要用于拦截用户的 url 请求, 并在Controller方法执行前后加入某些特殊处理,例如通过拦截器进行权限验证、记录请求信息的日志、判断用户是否登录等。类似于Filter过滤器。
拦截器的两种实现方式
- 1、继承
HandlerInterceptorAdapter
抽象类 - 2、实现
HandlerInterceptor
接口
HandlerInterceptor接口定义了三个方法
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;
void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}
- preHandle(): 该方法在请求到达Handler(
Controller
)之前,先执行的前置处理方法。当其返回值为true时,放行request请求, 继续执行拦截器链的下一个拦截器。返回值为false, 则直接中断请求。 - postHandle(): 该方法在控制器方法调用之后,视图解析之前执行。可以通过该方法对请求域之中的 model(模型数据) 和 view(逻辑视图) 作出进一步的修改。
- afterCompletion(): 该方法在整个请求完成,即DispatcherServlet 渲染了对应的视图之后执行。该方法一般用于实现一些资源清理、记录日志信息等工作。
【Spring MVC拦截器的执行流程】
1、前端控制器DispatcherServlet接收客户端发送的request请求,
2、DispatcherServlet拿到request请求后, 将其交给Controller进行处理,在这个过程中,如果配置了拦截器,则会先调用拦截器的preHandler()方法进行拦截处理(登陆验证、日志记录. . .)。
【 拦截器链 】
如果配置了多个Interceptor拦截器, 则拦截器呈链式调用(与Filter过滤器链类似)。拦截器的执行顺序与spring mvc配置文件中拦截器的声明顺序有关, 遵从 先声明、先执行 原则。
- 1、拦截器的preHandler方法按顺序执行, postHandler和afterCompletion方法按逆序执行
- 2、第一个拦截器的preHandler()方法返回false,则直接中断请求,后面拦截器的preHandler()方法也不会执行了
- 3、第二个拦截器的preHandler()方法返回false,则直接拦截所有的url请求,后面拦截器的preHandler()方法也不会执行了,但前面preHandler方法返回值为true的拦截器的afterCompletion()方法还会执行。
【spring_mvc.xml各配置节点说明 】
<!--拦截器链-->
<mvc:interceptors>
<!--拦截器1-->
<mvc:interceptor>
<!-- 拦截所有URL请求 -->
<mvc:mapping path="/**"/>
<!--拦截所有以/xxController/say结尾的URL请求-->
<mvc:mapping path="/xxController/say"/>
<!--不拦截以/xxController/hello结尾的URL请求-->
<mvc:exclude-mapping path="/xxController/hello"/>
<!-- 指定自定义拦截器类 -->
<bean class="com.cd4356.controller.Interceptor1"/>
</mvc:interceptor>
<!--拦截器2-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.cd4356.controller.Interceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
- <mvc:interceptors> 拦截器链
- <mvc:interceptor> 指定一个过滤器
- <mvc:mapping>元素用于配置要拦截的请求
- <mvc:exclude-mapping>元素用来配置不拦截的请求, 一般是在<mvc:mapping>元素使用通配符时, 才使用该元素配合使用
- <bean> 用来指定拦截器完整的限定类名
- path 是<mvc:mapping>和<mvc:exclude-mapping>元素的属性, 用来指定请求路径。可以指定具体的路径, 并且 path属性支持通配符, 可以使用通配符指定
- /XX/xx 拦截所有以/XX/xx结尾的URL请求, 一般XX为Controller类上的映射路径,xx为方法上的映射路径
- 通配符
- /* 子目录下的所有URL请求路径, 不包括孙目录
- /**
登陆验证拦截案例
【User.java】,创建一个User实体类用于保存session信息
package com.cd4356.controller;
public class User {
private String account;
private String password;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
【LoginController.java】
package com.cd4356.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
@RequestMapping("/to_login")
public String toLogin(){
return "login";
}
@RequestMapping("/login")
public String login(@RequestParam String account, @RequestParam String password, HttpSession session){
//判断用户用户是否存在,如果存在则将信息封装到User对象中,然后将User对象保存到session中,不存在则重定向回登陆页面
if(account.equals("abc") && password.equals("abc")){
User user = new User();
user.setAccount(account);
user.setPassword(password);
session.setAttribute("user",user);
return "redirect:/home";
}
return "redirect:/to_login";
}
@RequestMapping("/home")
public String home(){
System.out.println("home");
return "home";
}
@RequestMapping("/hello")
public String hello(){
System.out.println("home");
return "hello";
}
}
【Interceptor.java】, 自定义登陆验证拦截器, 从session中获取SessionKey对应值, 验证用户是否登陆,已登录则放行, 未登陆则重定向到登陆界面
package com.cd4356.controller;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Interceptor implements HandlerInterceptor {
/**
* 在业务处理器处理请求之前被调用
* 如果返回false
* 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
* 如果返回true
* 执行下一个拦截器,直到所有的拦截器都执行完毕
* 再执行被拦截的Controller
* 然后再进入拦截器链,从最后一个拦截器往回执行所有的postHandle()
* 接着再从最后一个拦截器往回执行所有的afterCompletion()
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//不拦截URL中含有login的请求,直接放行
/*String url = httpServletRequest.getRequestURI();
if (url.toLowerCase().indexOf("login")>=0){
System.out.println(url);
return true;
}*/
//获取session信息,验证用户是否登陆,已登录则放行,未登陆则重定向到登陆界面
User user = (User) httpServletRequest.getSession().getAttribute("user");
if(user!=null){
return true;
}
httpServletResponse.sendRedirect("/to_login");
return false;
}
/**
* 在业务处理器处理请求执行完成后,生成物理视图之前执行的动作
* 可在modelAndView中加入数据,比如当前时间
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
/**
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
*
* 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
【spring_mvc.xml】, 注册拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--注解扫描-->
<context:component-scan base-package="com.cd4356.controller"/>
<!--拦截器链-->
<mvc:interceptors>
<!--登陆验证拦截器-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/to_login"/>
<mvc:exclude-mapping path="/login"/>
<bean class="com.cd4356.controller.Interceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
【web.xml】, 注册主要servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring_mvc.xml</param-value>
</init-param>
<!--web服务器启动时自动加载-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--拦截所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
【login.html】
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<meta charset="UTF-8">
<title>登陆</title>
<style>
.main{
width: 300px;
text-align: center;
padding: 20px;
margin-top: 30px;
margin-left: 30px;
box-shadow: 0 0 5px 5px #ccc;
}
</style>
</head>
<body>
<div class="main">
<p>登陆</p>
<form action="/login" method="post">
<p><input type="text" name="account" placeholder="账号:"></p>
<p><input type="password" name="password" placeholder="密码: "></p>
<input type="submit" value="提交">
</form>
</div>
</body>
</html>
【home.html】
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>home</title>
</head>
<body>
<h1>欢迎你,来到xxx主页面!</h1>
</body>
</html>
【hello.html】
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>home</title>
</head>
<body>
<h1>hello,你好!</h1>
</body>
</html>
【运行结果】, 由图可以看出, 在未登录之前, 直接在地址栏中输入具体的URL地址, 请求会被拦截并重定向回登陆页面, 或者登陆账号或密码错误时也会重定向回登陆页面。 在登陆成功后, 就可以通过在地址栏中输入具体的URL地址来访问到具体的页面。
【重点】
Spring MVC拦截器还有个重要关注点, 那就是Spring MVC拦截器不会拦截jsp页面请求, 那我们岂不是可以直接在地址栏中就可以访问到具体的jsp页面啦。