1.5(Spring MVC学习笔记) 拦截器(Interceptor)
一、拦截器
1.1拦截器简介
Spring中的拦截器类似Servlet中的过滤器(Filter),主要用于拦截用户请求,
并进行一定的处理操作(如验证权限、记录日志、设置编码方式等)。
1.2拦截器实现
SpringMVC实现拦截器主要通过实现HandlerInterceptor接口,
或者是继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来实现。
HandlerInterceptor接口中有三个方法:
public boolean preHanlder(){...}:该方法在执行控制类方法之前执行,
如果该方法返回值为false后续执行终止(拦截器,处理器,控制类都终止)。
public void postHandler(){...}:该方法在控制类中方法执之后执行。
public void afterCompletion(){...}:该方法最后执行,在视图渲染结束后执行。
例如用户发送一个请求执行控制类中某个方法,首先执行preHanlder方法。
然后判断preHandler方法的返回值为true则继续执行,为false则中止。
接着当控制类中方法执行完后,执行拦截器中的postHandler方法。
接着执行后续的视图解析器,渲染视图,最后执行拦截器中的afterCompletion().
1.3拦截器配置
定义好拦截器类后,在xml文件中配置即可。
配置元素:
<mvc:interceptors>:所有拦截器都配置在该元素下。
子元素为<mvc:interceptors>,一个子元素代表一个拦截器,
可配置多个
<mvc:interceptor>:配置单个拦截器
<mvc:mapping>:指定拦截器需要拦截的路径。
<mvc:exclude-mapping>:指定拦截器不需要拦截的路径。
path属性作用于<mvc:mapping><mvc:exclude-mapping>,
用于指定拦截路径。
<bean>:代表具体拦截器的实现类。
在<mvc:interceptors>中配置<bena>,则该拦截器为全局拦截器会拦截所有请求。
在<mvc:interceptor>元素中配置,为拦截指定的路径。
<!-- 配置拦截器 --> <mvc:interceptors> <!-- 配置在interceptors中为全局拦截器 --> <bean class = "com.springmvc.interceptor.CustomInterceptor"/> <!-- 单个拦截器 --> <mvc:interceptor> <!-- 配置拦截内容,拦截所有内容 --> <mvc:mapping path="/**"/> <!-- 配置不拦截内容 --> <mvc:exclude-mapping path=""/> <!-- 对应的拦截器 --> <bean class = "xxx.xxx.xxx"/> </mvc:interceptor> </mvc:interceptors>
下面看一个拦截器的例子
web.xml
<?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_3_1.xsd" version="3.1" metadata-complete="true"> <display-name>SpringMVC</display-name> <!-- 配置前端控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class > org.springframework.web.servlet.DispatcherServlet </servlet-class> <!-- 初始化时加载配置文件,该配置文件是在src目录下创建的。 --> <!-- <init-param> 该选项不配置,则做会自动寻找WEB-INF下名为springmvc-servlet.xml的文件。--> <!-- <param-name>contextConfigLocation</param-name>--> <!-- <param-value>classpath:springmvc-config.xml</param-value>--> <!--</init-param>--> <!-- 当前servlet与容器一起加载 --> <load-on-startup>1</load-on-startup> </servlet> <!-- 所有请求都会被前端控制器拦截--> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
CustomInterceptor.java(拦截器)
import java.io.PrintStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class CustomInterceptor implements HandlerInterceptor{ //在HadlerAdapter之前执行(简单的看就是在访问控制类之前执行) PrintStream out = System.out; @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { // TODO Auto-generated method stub out.println("preHandle..."); return true; } //在控制器方法调用后,视图解析器之前执行。 @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { // TODO Auto-generated method stub out.println("postHandle..."); } //最后执行, @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { out.println("afterCompletionHandle..."); } }
springmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 开启扫描 --> <context:component-scan base-package = "com.springmvc.*"></context:component-scan> <!-- testJson --> <!-- 配置注解驱动 --> <mvc:annotation-driven/> <!-- 指定某些文件不被前端控制器拦截,直接访问静态文件。 --> <!-- 设置视图处理器及其前缀后缀 --> <bean id = "viewResolver" class = "org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value = "/WEB-INF/jsp/"></property> <property name="suffix" value = ".jsp"></property> </bean> <!-- 配置拦截器 --> <mvc:interceptors> <!-- 配置在interceptors中为全局拦截器 --> <bean class = "com.springmvc.interceptor.CustomInterceptor"/> </mvc:interceptors> </beans>
控制类
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class InterceptorsControl { @RequestMapping("/testInterceptor") public String handler() { System.out.println("handler"); return "success"; } }
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.springmvc.binderList.*" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> success <br/> </body> </html>
1.4多个过滤器
多个过滤器配置与上述类似,只是在<mvc:interceptors>中配置多个<mvc:interceptor>即可。
关键是多个过滤器的执行顺序。
假设有拦截器1~n,
执行顺序为: pre(1) pre(2)....pre(n) post(n) post(n-1)....post(1) after(n)after(n-1)...after(1)
先将上述CustomInterceptor中的所有打印语句中添加一个数字1。
System.out.println("perHadler1")
在将Customlnterceptor.java文件复制,并改名为CustomInterceptor2.java
并将其中数字1改为2.
修改springmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <!-- 开启扫描 --> <context:component-scan base-package = "com.springmvc.*"></context:component-scan> <!-- testJson --> <!-- 配置注解驱动 --> <mvc:annotation-driven/> <bean id = "viewResolver" class = "org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value = "/WEB-INF/jsp/"></property> <property name="suffix" value = ".jsp"></property> </bean> <!-- 配置拦截器 --> <mvc:interceptors> <!-- 拦截器1 --> <mvc:interceptor> <mvc:mapping path="/testInterceptor"/> <bean class = "com.springmvc.interceptor.CustomInterceptor"/> </mvc:interceptor> <!-- 拦截器2 --> <mvc:interceptor> <mvc:mapping path="/testInterceptor"/> <bean class = "com.springmvc.interceptor.CustomInterceptor2"/> </mvc:interceptor> </mvc:interceptors> </beans>
二、拦截器登录注册案例
User.java(POJO类)
public class User { private int id; private String userName; private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
Validate.java(拦截器)
拦截所有请求进行判断,
如果是去往登录页面或是登录验证放行,
如果已登录放行,
其它则跳转到登录页面。
未登录情况下无法任何资源页面,只能访问登录页面
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class Validate implements HandlerInterceptor{ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3) throws Exception { // TODO Auto-generated method stub } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3) throws Exception { // TODO Auto-generated method stub } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { // TODO Auto-generated method stub System.out.println("into interceptor"); String url = request.getRequestURI(); HttpSession session = request.getSession(); //去往登录页面放行 或者是登录验证放行 if(url.indexOf("/login") > 0 || url.indexOf("/checkLogin") > 0) return true; //已登录放行 if(session.getAttribute("user") != null) return true; //既不是去登录页面,同时未登录则跳转到登录页面,并提升未登录,请登录。 request.setAttribute("msg", "未登录,请登录"); request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; } }
UserController.java(控制类)
import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class UserController { //登录验证 @RequestMapping("/checkLogin") public String checkLogin(User user, Model model, HttpSession session) { String userName = user.getUserName(); String password = user.getPassword(); //模拟数据查询 if(userName != null && password != null && userName.equals("hcf") && password.equals("123456")) { //登录成功后设置session用于记录登录状态 session.setAttribute("user", user); return "main"; } model.addAttribute("msg", "用户名或密码错误或为空!"); return "login"; } //跳转main @RequestMapping("/main") public String toMain() { return "main"; } //跳转登录页面 @RequestMapping("/login") public String login() { return "login"; } //注销用户,即销毁session @RequestMapping("/loginOut") public String loginOut(HttpSession session) { session.invalidate(); return "login"; } }
login.jsp (登录页面)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> ${msg}<br> <!--表达提交后进行登录验证--> <form action = "${pageContext.request.contextPath}/checkLogin" method = "post"> 用户名:<input type = "text" name = "userName"/><br/> <!--name属性的值要和POJO类中属性名相同才可自动填充--> 密 码:<input type = "text" name = "password"/><br/> <input type = "submit" value = "登录"/> </form> </body> </html>
main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> weclome:<br> ${sessionScope.user.userName} <a href = "${pageContext.request.contextPath}/loginOut">注销</a> </body> </html>
用户请求访问main.jsp,会被拦截,判断是否登录。
如果登录则放行,执行控制类中的跳转主页方法。
如果未登录则设置msg值,并跳转登录页面。
用户访问登录页面直接放行。
登录页面提交表单后也会被拦截,拦截器判断为登录检查放行。
控制类中会对用户名及密码进行检查。
密码正确设置session保持登录状态,并跳转到main.jsp.
密码错误则设置msg值,并跳转到登录页面。