SpringMVC 学习记录
留神:以下内容是学习 北京能源节点 的 SpringMVC 视频后所记录的笔记、源码以及集体的了解等,记录下来仅供学习
_
第 4 章 SpringMVC 核心技术
4.3 拦截器
SpringMVC 中的 Interceptor 拦截器是十分重要和相当有用的,它的次要作用是拦挡指定的用户申请,并进行相应的预处理与后处理。其拦挡的工夫点在“处理器映射器依据用户提交的申请映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。当然,在处理器映射器映射出所要执行的处理器类时,曾经将拦截器与处理器组合为了一个处理器执行链,并返回给了地方调度器。
4.3.1 一个拦截器的执行
自定义拦截器
package com.bjpowernode.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
// 拦截器类:拦挡用户的申请。public class MyInterceptor implements HandlerInterceptor {
private long btime = 0;
/*
* preHandle 叫做预处理办法。*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {btime = System.currentTimeMillis();
System.out.println("拦截器的 MyInterceptor 的 preHandle()");
// 计算的业务逻辑,依据计算结果,返回 true 或者 false
// 给浏览器一个返回后果 //request.getRequestDispatcher("/tips.jsp").forward(request,response);
return true;
}
/*
postHandle: 后处理办法。*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler, ModelAndView mv) throws Exception {System.out.println("拦截器的 MyInterceptor 的 postHandle()");
// 对原来的 doSome 执行后果,须要调整。if(mv != null){
// 批改数据
mv.addObject("mydate",new Date());
// 批改视图
mv.setViewName("other");
}
}
/*
afterCompletion: 最初执行的办法
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {System.out.println("拦截器的 MyInterceptor 的 afterCompletion()");
long etime = System.currentTimeMillis();
System.out.println("计算从 preHandle 到申请解决实现的工夫:"+(etime - btime));
}
}
自定义拦截器,须要实现 Handlerinterceptor 接口。而该接口中含有三个办法:
1、preHandle(request,response, Object handler) 预处理办法:
该办法在处理器办法执行之前执行。其返回值为 boolean,若为 true,则紧接着会执行处理器方 法,且会将 afterCompletion()办法放入到一个专门的办法栈中期待执行。
重要:是整个我的项目的入口,门户。当 preHandle 返回 true 申请能够被解决。preHandle 返回 false,申请到此办法就截止。参数:Object handler:被拦挡的控制器对象
返回值 boolean
true:申请是通过了拦截器的验证,能够执行处理器办法。拦截器的 MyInterceptor 的 preHandle()
===== 执行 MyController 中的 doSome 办法 =====
拦截器的 MyInterceptor 的 postHandle()
拦截器的 MyInterceptor 的 afterCompletion()
false:申请没有通过拦截器的验证,申请达到拦截器就截止了。申请没有被解决
拦截器的 MyInterceptor 的 preHandle()
特点:a. 办法在控制器办法(MyController 的 doSome)之前先执行的。用户的申请首先达到此办法
b. 在这个办法中能够获取申请的信息,验证申请是否符合要求。能够验证用户是否登录,验证用户是否有权限拜访某个连贯地址(url)。如果验证失败,能够截断申请,申请不能被解决。如果验证胜利,能够放行申请,此时控制器办法能力执行。
2、postHandle(request/response/ Object handlei,modelAndView) 后处理办法:
该办法在处理器办法执行之后执行。处理器办法若最终未被执行,则该办法不会执行。因为该办法是在处理器办法执行完后执行,且该办法参数中蕴含 ModelAndView,所以该办法能够修 改处理器办法的处理结果数据,且能够批改跳转方向。
参数:Object handler:被拦挡的处理器对象 MyController
ModelAndView mv: 处理器办法的返回值
特点:a. 在处理器办法之后执行的(MyController.doSome())b. 可能获取到处理器办法的返回值 ModelAndView, 能够批改 ModelAndView 中的
数据和视图,能够影响到最初的执行后果。c. 次要是对原来的执行后果做二次修改,ModelAndView mv = MyController.doSome();
postHandle(request,response,handler,mv);
3、afterCompletion(request,response, Object handler, Exception ex)最初执行的办法:
当 preHandle()办法返回 true 时,会将该办法放到专门的办法栈中,等到对申请进行响应的所有 工作实现之后才执行该办法。即该办法是在地方调度器渲染 (数据填充) 了响应页面之后执行的,此 时对 ModelAndView 再操作也对响应杯水车薪。
afterCompletion** 最初执行的办法,革除资源,例如在 Controller 办法中退出数据
参数
Object handler: 被拦截器的处理器对象
Exception ex:程序中产生的异样
特点:
a. 在申请解决实现后执行的。框架中规定是当你的视图解决实现后,对视图执行了 forward。就认为申请解决实现。b. 个别做资源回收工作的,程序申请过程中创立了一些对象,在这里能够删除,把占用的内存回收。
拦截器中办法与处理器办法的执行程序如下图:
换一种体现形式,也能够这样了解:
(1) 注册拦截器
springmvc.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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 申明组件扫描器 -->
<context:component-scan base-package="com.bjpowernode.controller" />
<!-- 申明 springmvc 框架中的视图解析器,帮忙开发人员设置视图文件的门路 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀:视图文件的门路 -->
<property name="prefix" value="/WEB-INF/view/" />
<!-- 后缀:视图文件的扩展名 -->
<property name="suffix" value=".jsp" />
</bean>
<!-- 申明拦截器:拦截器能够有 0 或多个 -->
<mvc:interceptors>
<!-- 申明第一个拦截器 -->
<mvc:interceptor>
<!-- 指定拦挡的申请 uri 地址
path:就是 uri 地址,能够应用通配符 **
**:示意任意的字符,文件或者多级目录和目录中的文件
http://localhost:8080/myweb/user/listUser.do
http://localhost:8080/myweb/student/addStudent.do
-->
<mvc:mapping path="/**"/>
<!-- 申明拦截器对象 -->
<bean class="com.bjpowernode.handler.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
<mvc:mapping/> 用于指定以后所注册的拦截器能够拦挡的申请门路,而 /** 示意拦挡所 有申请。
(2) 批改 index 页面
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String basePath = request.getScheme() + "://" +
request.getServerName() + ":" + request.getServerPort() +
request.getContextPath() + "/";
%>
<html>
<head>
<title>Title</title>
<base href="<%=basePath%>" />
</head>
<body>
<p> 一个拦截器 </p>
<form action="some.do" method="post">
姓名:<input type="text" name="name"> <br/>
年龄:<input type="text" name="age"> <br/>
<input type="submit" value="提交申请">
</form>
</body>
</html>
(3) 批改处理器
MyController.java
package com.bjpowernode.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* @RequestMapping:
* value:所有申请地址的公共局部,叫做模块名称
* 地位:放在类的下面
*/
@Controller
public class MyController {@RequestMapping(value = "/some.do")
public ModelAndView doSome(String name,Integer age) {System.out.println("===== 执行 MyController 中的 doSome 办法 =====");
// 解决 some.do 申请了。相当于 service 调用解决实现了。ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
mv.setViewName("show");
return mv;
}
}
(4) 批改 show 页面
show.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>/WEB-INF/view/show.jsp 从 request 作用域获取数据 </h3><br/>
<h3>myname 数据:${myname}</h3><br/>
<h3>myage 数据:${myage}</h3>
</body>
</html>
4.3.2 多个拦截器的执行
我的项目在 4.3.1 的根底上
我的项目构造
(1) 再定义一个拦截器
MyInterceptor2 .java
package com.bjpowernode.handler;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 拦截器类:拦挡用户的申请。public class MyInterceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("22222- 拦截器的 MyInterceptor 的 preHandle()");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler, ModelAndView mv) throws Exception {System.out.println("22222- 拦截器的 MyInterceptor 的 postHandle()");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {System.out.println("22222- 拦截器的 MyInterceptor 的 afterCompletion()");
}
}
(2) 多个拦截器的注册与执行
springmvc.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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 申明组件扫描器 -->
<context:component-scan base-package="com.bjpowernode.controller" />
<!-- 申明 springmvc 框架中的视图解析器,帮忙开发人员设置视图文件的门路 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀:视图文件的门路 -->
<property name="prefix" value="/WEB-INF/view/" />
<!-- 后缀:视图文件的扩展名 -->
<property name="suffix" value=".jsp" />
</bean>
<!-- 申明拦截器:拦截器能够有 0 或多个
在框架中保留多个拦截器是 ArrayList,依照申明的先后顺序放入到 ArrayList
-->
<mvc:interceptors>
<!-- 申明第一个拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- 申明拦截器对象 -->
<bean class="com.bjpowernode.handler.MyInterceptor" />
</mvc:interceptor>
<!-- 申明第二个拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.bjpowernode.handler.MyInterceptor2" />
</mvc:interceptor>
</mvc:interceptors>
</beans>
当有多个拦截器时,造成拦截器链。拦截器链的执行程序,与其注册程序统一。须要再次强调一点的是,当某一个拦截器的 peHandle()办法返回 true 并被执行到时,会向一个专门的办法栈中放入该拦截器的 afterCompletion()办法。
多个拦截器中办法与处理器办法的执行程序如下图:
从图中能够看出,只有有一个 preHandle()办法返回 false,则上部的执行链将被断开,其后续的处理器办法与 postHandle()办法将无奈执行。但,无论执行链执行状况怎么,只有 办法栈中有办法,即执行链中只有有 preHandle()办法返回 true, 就会执行办法栈中的 afterCompletion()办法。最终都会给出响应。
换一种体现形式,也能够这样了解:
4.3.3 公共资源
(1) pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>ch11-interceptor2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--servlet 依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--jsp 依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
<scope>provided</scope>
</dependency>
<!--springmvc 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 编码和编译和 JDK 版本 -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
(2) 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_4_0.xsd"
version="4.0">
<!-- 申明,注册 springmvc 的外围对象 DispatcherServlet
须要在 tomcat 服务器启动后,创立 DispatcherServlet 对象的实例。为什么要创立 DispatcherServlet 对象的实例呢?因为 DispatcherServlet 在他的创立过程中,会同时创立 springmvc 容器对象,读取 springmvc 的配置文件,把这个配置文件中的对象都创立好,当用户发动
申请时就能够间接应用对象了。servlet 的初始化会执行 init()办法。DispatcherServlet 在 init()中{
// 创立容器,读取配置文件
WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
// 把容器对象放入到 ServletContext 中
getServletContext().setAttribute(key, ctx);
}
启动 tomcat 报错,读取这个文件 /WEB-INF/springmvc-servlet.xml(/WEB-INF/myweb-servlet.xml)springmvc 创立容器对象时,读取的配置文件默认是 /WEB-INF/<servlet-name>-servlet.xml .
-->
<servlet>
<servlet-name>myweb</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 自定义 springmvc 读取的配置文件的地位 -->
<init-param>
<!--springmvc 的配置文件的地位的属性 -->
<param-name>contextConfigLocation</param-name>
<!-- 指定自定义文件的地位 -->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 在 tomcat 启动后,创立 Servlet 对象
load-on-startup: 示意 tomcat 启动后创建对象的程序。它的值是整数,数值越小,tomcat 创建对象的工夫越早。大于等于 0 的整数。-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myweb</servlet-name>
<!--
应用框架的时候,url-pattern 能够应用两种值
1. 应用扩展名形式,语法 *.xxxx , xxxx 是自定义的扩展名。罕用的形式 *.do, *.action, *.mvc 等等
不能应用 *.jsp
http://localhost:8080/myweb/some.do
http://localhost:8080/myweb/other.do
2. 应用斜杠 "/"
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>