概述 :
拦截器的作用:
拦截器在Spring MVC中的作用主要包括以下几个方面:
-
权限控制:拦截器可以用来对请求进行权限验证,比如检查用户是否登录、是否具有访问某个资源的权限等。
-
日志记录:拦截器可以用来记录请求的相关信息,比如请求的URL、请求参数、处理时间等,方便后续的监控和分析。
-
参数验证:拦截器可以在请求处理之前对参数进行验证,确保参数的合法性,避免不必要的错误发生。
-
统一处理:拦截器可以用来实现一些通用的功能,比如统一异常处理、统一返回结果格式等,提高代码的复用性和可维护性。
拦截器的实现原理:
拦截器的实现原理主要基于Spring MVC框架的HandlerInterceptor接口。当客户端发送请求时,请求会经过DispatcherServlet,DispatcherServlet会根据请求的URL找到对应的处理器(Controller),在执行处理器之前和之后会调用拦截器的preHandle和postHandle方法。
具体实现原理如下:
-
定义拦截器:我们可以通过实现HandlerInterceptor接口来定义自己的拦截器,该接口包括preHandle、postHandle和afterCompletion三个方法,分别用于在处理器执行之前、之后和完成之后执行相应的逻辑。
-
配置拦截器:在Spring MVC的配置文件中,我们可以通过配置WebMvcConfigurerAdapter类的addInterceptors方法来添加拦截器,并指定拦截器的拦截规则和顺序。
-
执行拦截器:当客户端发送请求时,DispatcherServlet会根据请求的URL找到对应的处理器,并在执行处理器之前调用拦截器的preHandle方法,如果preHandle方法返回true,则继续执行处理器,否则终止请求。处理器执行完成后,会调用拦截器的postHandle方法进行后续处理,最后调用afterCompletion方法进行资源清理。
拦截器内主要方法:
在Spring MVC中,拦截器主要包含以下几个主要方法:
-
preHandle方法:在处理器执行之前调用,用于对请求进行预处理。该方法返回一个布尔值,如果返回true,则继续执行处理器;如果返回false,则终止请求。可以在该方法中进行权限校验、参数验证等操作。
-
postHandle方法:在处理器执行之后、视图渲染之前调用(也就是handler中 代码return”页面“跳转之前),可以对处理器的执行结果进行后续处理。可以通过该方法修改ModelAndView对象,添加额外的数据或修改视图。
-
afterCompletion方法:在整个请求处理完成后调用,即视图渲染完成后。可以用于清理资源、记录日志等操作。该方法在preHandle方法返回true时才会被调用。
通过实现这些方法,我们可以在拦截器中实现一些通用的功能,比如权限控制、日志记录、参数验证等。这些方法的调用顺序为preHandle -> 处理器执行 -> postHandle -> 视图渲染 -> afterCompletion。通过合理实现这些方法,可以实现灵活、高效的请求处理逻辑。
原理视图:
拦截器执行流程:
单个拦截器:
多个拦截器:
在Spring MVC中,如果配置了多个拦截器,它们的执行顺序是按照配置的顺序依次执行的。具体的执行流程如下:
-
当客户端发送请求时,请求会经过DispatcherServlet。
-
DispatcherServlet会根据请求的URL找到对应的处理器(Controller)。
-
在执行处理器之前,会按照配置的顺序依次执行所有配置的拦截器的preHandle方法。如果其中任何一个拦截器的preHandle方法返回false,则后续的拦截器的preHandle方法和处理器都不会执行,请求直接结束。
-
如果所有拦截器的preHandle方法都返回true,则执行处理器的方法。
-
处理器执行完成后,会按照配置的顺序逆序执行所有拦截器的postHandle方法。
-
最后,会按照配置的顺序逆序执行所有拦截器的afterCompletion方法。在这个阶段,可以进行一些资源清理的操作。
注意:我们可以观察到:对于多个拦截器的执行,都是从第一个拦截器开始顺序进行,但是psthandle和aftercompletion都是逆序完成配置
实际操作:
1、配置拦截器(springmvc-servlet.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.j2ee.controller" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/" />
<property name="suffix" value=".jsp" />
</bean>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/login"/>
<!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->
<bean class="com.j2ee.interceptor.LoginInterceptor"/>
</mvc:interceptor>
<!-- 当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法 -->
</mvc:interceptors>
<!-- <bean id="simpleUrlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/index">indexController</prop>
</props>
</property>
</bean>
<bean id="indexController" class="com.j2ee.controller.IndexController"></bean> -->
</beans>
注意:mvc:需要配置好约束,也就是文本中xmls里的约束内容
2、创建拦截器类
package com.j2ee.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class LoginInterceptor extends HandlerInterceptorAdapter{
/**
* 在业务处理器处理请求之前被调用
* 如果返回false
* 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
* 如果返回true
* 执行下一个拦截器,直到所有的拦截器都执行完毕
* 再执行被拦截的Controller
* 然后进入拦截器链,
* 从最后一个拦截器往回执行所有的postHandle()
* 接着再从最后一个拦截器往回执行所有的afterCompletion()
*/
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
String username = (String)session.getAttribute("userName");
if(username!=null)
return true;
request.setAttribute("msg","您还没有登陆,请登陆!");
request.getRequestDispatcher("/WEB-INF/page/login.jsp").forward(request,response);
return false;
}
/**
* 在业务处理器处理请求执行完成后,生成视图之前执行的动作
* 可在modelAndView中加入数据,比如当前时间
*/
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
/**
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
*
* 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
*/
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
3、创建登录Web页面(login.jsp,位置:WEB-INF/page)
<%@ page language="java" import="java.util.*" isELIgnored="false" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
</head>
<body>
<h2>登陆</h2>
<h3>${msg}</h3>
<form action="${pageContext.request.contextPath}/addLogin} method="post">
用户 :<input type="text" name="name" value=""><br />
密码: <input type="password" name="password" value=""><br />
<input type="submit" value="登陆">
</form>
</body>
</html
4、添加接收数据的方法。
package com.j2ee.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.j2ee.pojo.Users;
@Controller
public class LoginController {
@RequestMapping("login")
public ModelAndView login(){
ModelAndView mav = new ModelAndView("login");
return mav;
}
@RequestMapping("addLogin")
public ModelAndView recieve(Users users,HttpSession session){
ModelAndView mav = new ModelAndView();
String name = users.getName();
String passwd = users.getPassword();
if("admin".equals(name) && "888".equals(passwd)){
session.setAttribute("userName",name);
mav.addObject("name", name);
mav.setViewName("recieve");
}else{
mav.addObject("msg", "用户名和密码错误!");
mav.setViewName("login");
}
return mav;
}
}
5、创建接收数据Web页面(recieve.jsp,位置:WEB-INF/page)
<%@ page language="java" import="java.util.*" isELIgnored="false" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>显示结果</title>
</head>
<body>
<h2>session存储的用户信息:${name}</h2>
<h3>user:${users.name}</h3>
<h3>pwd:${users.password}</h3>
</body>
</html>
6、测试:
配置好tomcat并开始输入测试,输入/login后缀开始测试