SpringMvc
Spring MVC是由Spring官方提供的基于MVC设计理念的web框架
一、SpringMVC概述
1.1 SpringMVC优势
- 严格遵守了MVC分层思想
- 采用了松耦合、插件式结构;相比较于我们封装的BaseServlet以及其他的一些MVC框架来说更灵活、更具扩展性
- SpringMVC是基于Spring的扩展、提供了一套完善的MVC注解
- SpringMVC在数据绑定、视图解析都提供了多种处理方式,可灵活配置
- SpringMVC对RESTful URL设计方法提供了良好的支持
1.2 SpringMVC本质工作
- 接收并解析请求
- 处理请求
- 数据渲染、响应请求
二、SpringMVC框架部署
2.1 基于Maven创建一个Web工程
2.2 添加SpringMVC依赖
- spring-context
- spring-aspects
- spring-jdbc
- spring-web
- spring-webmvc
- spring-junit
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.9</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.0.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.9</version>
</dependency>
</dependencies>
2.3 创建SpringMVC配置文件
- 在resource目录下创建名为spring-servlet.xml 文件
- 添加MVC命名空间
spring-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:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 声明使用注解配置-->
<context:annotation-config/>
<!-- 声明Spring工厂注解的扫描范围-->
<context:component-scan base-package="com.liguoqing"/>
<!-- 声明mvc使用注解驱动-->
<mvc:annotation-driven/>
</beans>
2.4 在web.xml中配置SpringMVC的前端控制器
SpringMVC提供了一个名为DispatcherServlet的类(SpringMVC前端控制器),用于拦截用户请求交由SpringMVC处理
webapp/WEB-INF/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">
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- /* 拦截所有的HTTP请求,包括.jsp的请求,都做为控制器类的请求路径来处理-->
<!-- / 拦截所有的HTTP请求,但不包括.jsp的请求,但不会放行静态资源请求,html/js/css/图片-->
</web-app>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
三、SpringMVC框架使用
在SpringMVC中,我们把接收用户请求,处理用户请求的类称之为Controller(控制器)
3.1 创建控制器
- 创建一个名为 com.liguoqing.controllers 的包(包需要在Spring注解扫描的范围内)
- 创建一个类(无需做任何的继承和实现)
- 在类上添加 @Controller 注解声明此类为SpringMVC的控制器
- 在类上添加 @RequestMapping("/url") 声明此控制器类的请求url
@Controller
@RequestMapping("/book")
public class BookController {
}
- bean类
package com.xie.bean;
public class Book {
private int bookId;
private String bookName;
private String bookAuthor;
private double bookPrice;
public Book(int bookId, String bookName, String bookAuthor, double bookPrice) {
this.bookId = bookId;
this.bookName = bookName;
this.bookAuthor = bookAuthor;
this.bookPrice = bookPrice;
}
public Book() {
}
@Override
public String toString() {
return "Book{" +
"bookId=" + bookId +
", bookName='" + bookName + ''' +
", bookAuthor='" + bookAuthor + ''' +
", bookPrice=" + bookPrice +
'}';
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookAuthor() {
return bookAuthor;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
public double getBookPrice() {
return bookPrice;
}
public void setBookPrice(double bookPrice) {
this.bookPrice = bookPrice;
}
}
3.2 在控制器类中定义处理请求的方法
- 在一个控制器类中可以定义多个方法处理不同的请求
- 在每个方法上添加 @RequestMapping("/url") 用于声明当前方法请求的url
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/add")
public void add(){
}
@RequestMapping("/list")
public void list(){
}
}
3.3 静态资源配置
静态资源:就是项目中的HTML、CSS、JS、图片等
3.3.1 /* 和 / 区别
<!-- /* 拦截所有的HTTP请求,包括.jsp的请求,都做为控制器类的请求路径来处理-->
<!-- / 拦截所有的HTTP请求,但不包括.jsp的请求,但不会放行静态资源请求(html/js/css/图片)-->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
3.3.2 静态资源放行配置
- 在springMVC的配置文件中,添加如下静态资源放行的配置
spring-servlet.xml
<!--放行资源-->
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/pages/**" location="/pages/"/>
<mvc:resources mapping="/imgs/**" location="/imgs/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
3.4 前端提交数据到控制器
3.4.1 表单提交
- 创建前端页面(book-add.jsp)
表单提交:输入框需要提供name属性,springMVC控制器是通过name属性取值的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>添加图书</h3>
<form action="book/add" method="post">
<p>图书名称:<input type="text"></p>
<p>图书作者:<input type="text"></p>
<p>图书价格:<input type="text"></p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
3.4.2 URL提交
URL提交:
<%--超链接提交--%>
<a href="book/add?bookName=Java">URL提交</a>
3.4.3 AJAX提交
AJAX提交:请求行、请求头、请求体都可以用来传值
<h3>AJAX提交</h3>
<input type="button" value="ajax提交"/>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$("#btn1").click(function (){
var obj ={};
obj.bookName="Java";
obj.bookAuthor="张三";
obj.bookPrice=3.33;
$.ajax({
url:"book/add",
type:"post",
headers:{
},
contentType:"application/json",
data:obj,
success:function (res){
console.log(res);
}
});
});
</script>
3.5 控制器中接收前端提交的数据
3.5.1 请求行传值
- 表单提交 method="get"
- URL提交
- $.ajax请求的url传值
$.ajax({
url:这里拼接url,把参数放url就是请求行传值,
type:"post",
headers:{
},
contentType:"application/json",
data:obj,
success:function (res){
console.log(res);
}
});
$.post()/$.get()中的{}传值
@RequestParam 注解用于接收请求行传递的数据
- 前端提交数据
<h3>表单提交</h3>
<form action="book/add" method="get">
<p>图书名称:<input type="text" name="name"></p>
<p>图书作者:<input type="text" name="author"></p>
<p>图书价格:<input type="text" name="price"></p>
<p><input type="submit" value="提交"></p>
</form>
- 控制器接收数据
package com.liguoqing.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/book")
public class BookController {
// <form action="book/add" method="post">
// <p>图书名称:<input type="text" name="name"></p>
// <p>图书作者:<input type="text" name="author"></p>
// <p>图书价格:<input type="text" name="price"></p>
// <p><input type="submit" value="提交"></p>
// </form>
/*接收请求行数据*/
@RequestMapping("/add")
public void add(@RequestParam("name") String a,
@RequestParam("author") String b,
@RequestParam("price") double c){
System.out.println("~~~~~book add");
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
注意:
如果控制器方法中接收数据的参数名与请求行传值的key是一致的,则@RequestParam注解可以省略
@RequestMapping("/add")
public void add(String name, String author, double price){
System.out.println("~~~~~book add");
System.out.println(name);
System.out.println(author);
System.out.println(price); }
3.5.2 请求头传值
- $.ajax 封装请求头数据
$.ajax({
url:"book/add",
type:"post",
headers:{ 这里请求头传值 },
contentType:"application/json",
data:obj,
success:function (res){
console.log(res);
}
});
演示代码:(这里的请求头里如果写中文暂时是有问题的)
@RequestHeader 用于接收请求头传递的数据
@RequestMapping("/list")
public void list(@RequestHeader("token") String token){
System.out.println(token);
System.out.println("~~~~~book list");
}
-----------------------------------------------------------------
<h3>AJAX提交</h3>
<input type="button" value="ajax提交" id="btn1"/>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$("#btn1").click(function (){
$.ajax({
url:"book/list",
type:"post",
headers:{
token:"Eqeqeq"
},
success:function (res) {
console.log(res);
}
});
console.log("结束");
});
</script>
3.5.3 请求体传值
- $.ajax 封装请求体数据
$.ajax({
url:"book/add",
type:"post",
headers:{ 这里请求头传值 },
contentType:"application/json",
data:{
这里放请求体数据
},
success:function (res){
console.log(res);
} });
@RequestBody 注解用于接收请求体传递的数据
- 前端
<h3>AJAX提交</h3>
<input type="button" value="ajax提交" id="btn1">
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$("#btn1").click(function (){
console.log("进入方法");
var obj ={};
obj.bookName="Java";
obj.bookAuthor="张三";
obj.bookPrice=3.33;
console.log("obj");
$.ajax({
url:"book/update",
type:"post",
contentType:"application/json",
data:JSON.stringify(obj),
success:function (res) {
console.log(res);
}
});
console.log("结束");
})
- 导入依赖
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.1</version>
</dependency>
- 测试
@RequestMapping("/update")
public void update(@RequestBody Book book){
// servlet的处理方式使用request的输入流接收请求体数据
System.out.println(".....book update");
System.out.println(book);
}
3.6 控制器响应前端请求
3.6.1 控制器响应同步请求
同步请求:form , 超链接
-
处理同步请求的方法的返回类型定义为String或者ModelAndView,以实现页面的跳转
- 返回类型为String:
-
新建一个jsp(tips.jsp)
<h3>表单提交</h3> <form action="book/add" method="get"> <p>图书名称:<input type="text" name="name"></p> <p>图书作者:<input type="text" name="author"></p> <p>图书价格:<input type="text" name="price"></p> <p><input type="submit" value="提交"></p> </form><%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>跳转成功</h1> </body> </html>- 转发
@RequestMapping("/add") public String add( @RequestParam("name") String a, @RequestParam("author") String b, @RequestParam("price") double c){ System.out.println(".....book add"); return "/tips.jsp"; }- 重定向
@RequestMapping("/add") public String add( @RequestParam("name") String a, @RequestParam("author") String b, @RequestParam("price") double c){ System.out.println(".....book add"); return "redirect:/tips.jsp"; } -
返回类型为ModelAndView
- 转发
@RequestMapping("/add") public ModelAndView add( @RequestParam("name") String a, @RequestParam("author") String b, @RequestParam("price") double c){ System.out.println(".....book add"); ModelAndView modelAndView = new ModelAndView("/tips.jsp"); return modelAndView; }- 重定向
@RequestMapping("/add") public ModelAndView add( @RequestParam("name") String a, @RequestParam("author") String b, @RequestParam("price") double c){ System.out.println(".....book add"); ModelAndView modelAndView = new ModelAndView("redirect:/tips.jsp"); return modelAndView; }
3.6.2 控制器响应异步请求
异步请求:ajax请求
1. 使用response中的输出流进行响应
- 控制器方法的返回类型为void
- 控制器方法添加HttpServletResponse response 参数
- 在方法中通过response 获取输出流,使用流响应ajax请求
@RequestMapping("/update")
public void update(@RequestBody Book book, HttpServletResponse response) throws IOException {
response.setContentType("text/plain");
response.setContentType("application/json");
System.out.println(".....book update");
System.out.println("请求头传递:"+book);
String s = new ObjectMapper().writeValueAsString(book);
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
out.println(s);
out.flush();
out.close();
}
<h3>AJAX提交</h3>
<input type="button" value="ajax提交" id="btn1"/>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$("#btn1").click(function (){
console.log("进入方法");
var obj ={};
obj.bookName="Java";
obj.bookAuthor="张三";
obj.bookPrice=3.33;
console.log("obj");
$.ajax({
url:"book/update",
type:"post",
headers:{
token:"Eqeqeq"
},
contentType: "application/json;charset=UTF-8",
data:JSON.stringify(obj),
dataType: "json",
success: function(response) {
console.log(response);
alert("请求成功!")
},
// error: 设置请求失败后的回调函数
error: function() {
alert("请求失败!请稍后再试!");
},
success:function (res) {
console.log(res);
}
});
console.log("结束");
});
</script>
2. 直接在控制器方法返回响应的对象
- 控制器方法的返回类型设置为响应给ajax请求的对象类型
- 在控制器方法前添加一个 @ResponseBody 注解,将返回的对象转换成JSON格式返回给ajax请求
- 如果一个控制器类中的所有方法都是响应ajax请求,则可以直接在控制器类前添加 @ResponseBody 注解
@RequestMapping("/update2")
@ResponseBody
public List<Book> update2(@RequestBody Book book, HttpServletResponse response) throws IOException {
// servlet的处理方式使用request的输入流接收请求体数据
System.out.println(".....book update2");
System.out.println("直接在控制器方法返回响应的对象");
List<Book> books=new ArrayList<Book>();
books.add(new Book(1,"java","老张",22));
books.add(new Book(2,"C语言","老李",82));
System.out.println(books);
return books;
}
3.6.3 控制器响应数据传递(同步请求)
3.6.3.1 返回类型是String
方法一:Model
对于同步请求的转发响应,我们可以传递参数到转发的页面
-
返回类型为String:
- 1:在控制器方法中定义一个Model类型的参数
- 2:在return页面之前,向model中添加键值对,添加的键值对就会被传递到转发的页面
@RequestMapping("/add2") public String add2( @RequestParam("name") String a, @RequestParam("author") String b, @RequestParam("price") double c, Model model){ model.addAttribute("key1","key2"); model.addAttribute("book",new Book(1,"java","老张",22)); System.out.println(".....book2 add"); return "/tips.jsp"; }<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h3>表单提交</h3> <form action="book/add3" method="get"> <p>图书名称:<input type="text" name="name"></p> <p>图书作者:<input type="text" name="author"></p> <p>图书价格:<input type="text" name="price"></p> <p><input type="submit" value="提交"></p> </form><%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>跳转页面</title> </head> <body> <h1>恭喜跳转成功</h1> <hr> ${key1} <hr> ${book.bookName} </body> </html>
方法二:request
@RequestMapping("/add4")
public String add4(String name, String author, double price, HttpServletRequest request){
request.setAttribute("key1","key2");
request.setAttribute("book",new Book(1,"java","老张",22));
System.out.println(".....book2 add");
return "/tips.jsp";
}
3.6.3.2 返回类型是ModelAndview
@RequestMapping("/add5")
public ModelAndView add5(String name, String author, double price){
ModelAndView modelAndView = new ModelAndView("/tips.jsp");
modelAndView.addObject("key1","value1");
modelAndView.addObject("book",new Book(1,"java","老张",22));
return modelAndView;
}
3.7 解决中文乱码问题
3.7.1 前端代码
- JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
- HTML页面
<meta charset="UTF-8">
3.7.2 服务器编码
- tomcat/conf/
3.7.3 设置SpringMVC的编码方式
- 在web.xml中配置SpringMVC编码过滤器的编码方式
<filter> <filter-name>EncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>EncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
四、SpringMVC的请求处理流程
4.1 请求处理流程
SpringMVC 通过前端控制器(DispatcherServlet)拦截并处理用户请求的
- ①前端发送请求被前端控制器DispatcherServlet拦截
- ②前端控制器调用处理器映射器HandlerMapping对请求URL进行解析,解析之后返回调用给前端控制器
- ③前端控制器调用处理器适配器数处理调用链
- ④处理器适配器基于反射通过适配器设计模式完成处理器(控制器)的调用处理用户请求
- ⑤处理器适配器将控制器返回的视图和数据信息封装成ModelAndViewll响应给前端控制器
- ⑥前端控制器调用视图解析器ViewResolver对ModelAndViewi进行解析,将解析结果(视图资源和数据)响应给前端控制器
- ⑦前端控制器调用视图vⅵw组件将数据进行渲染,将渲染结果(静态视图)响应给前端控制器
- ⑧前端控制器响应用户请求
4.2 SpringMVC核心组件
- DispatcherServlet 前端控制器,总控制器
- 作用:接收请求,协同各组件工作,响应请求
- HandlerMapping 处理器映射
- 作用:负责根据用户请求的URL找到对应的Handler
- 可以自定义配置 SpirngMVC提供多个处理器映射的实现,可以根据需要进行配置
- HandlerAdapter 处理器适配器
- 作用:根据HandlerMapping 解析用户请求后产生的调用链,通过适配器模式完成Handler的调用
- Handler 处理器/控制器
- 由工程师根据业务的需求进行开发
- 作用:处理请求
- ModelAndView 视图模型
- 作用:用于封装处理器返回的数据以及响应的视图
- ModelAndView = Model + View
- ViewResolver 视图解析器
- 作用:对ModelAndView进行解析
- 可以自定义配置 SpirngMVC提供多个视图解析器的实现,可以根据需要进行配置
- View 视图
- 作用:完成数据渲染
4.3 处理器映射器
不同的处理器映射器对URL处理的方式也不相同,使用对应的处理器映射器之后,我们的前端请求规则也需要发生相应的变化
SpringMVC提供的处理器映射器:
1:BeanNameUrlHandlerMappering:根据控制器的Id访问控制器 (首字母小写)
2:SimpleUrlHandlerMapping 根据控制器配置的URL访问(默认)
配置处理器映射器:
- 在SpringMVC的配置文件中通过Bean标签声明处理器映射器
默认首字母小写,就不用写 @RequestMapping了<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
- 配置 BeanNameUrlHandlerMapping
相当于<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/book">bookController</prop> </props> </property> </bean>所以就默认不配,直接使用注解@RequestMapping("/book")
4.4 视图解析器
SpringMVC提供了多个视图解析器:
1:UrlBasedViewResolver
2:InternalResourceViewResolver
-
UrlBasedViewResolver 需要依赖 jstl 包
- 添加JSTL的依赖
pom.xml
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>配置
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean>prefix:前缀
suffix:后缀
如果配置了这个
则 -
InternalResourceViewResolver
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean>
五、 使用对象接收数据在控制器方法中
实体类
public class Book {
private int bookId;
private String bookName;
private String bookAuthor;
private double bookPrice;
private Date publishTime;
public Book() {
}
public Book(int bookId, String bookName, String bookAuthor, double bookPrice, Date publishTime) {
this.bookId = bookId;
this.bookName = bookName;
this.bookAuthor = bookAuthor;
this.bookPrice = bookPrice;
this.publishTime = publishTime;
}
public Book(int bookId, String bookName, String bookAuthor, double bookPrice) {
this.bookId = bookId;
this.bookName = bookName;
this.bookAuthor = bookAuthor;
this.bookPrice = bookPrice;
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookAuthor() {
return bookAuthor;
}
public void setBookAuthor(String bookAuthor) {
this.bookAuthor = bookAuthor;
}
public double getBookPrice() {
return bookPrice;
}
public void setBookPrice(double bookPrice) {
this.bookPrice = bookPrice;
}
public Date getPublishTime() {
return publishTime;
}
public void setPublishTime(Date publishTime) {
this.publishTime = publishTime;
}
}
book-add.jsp
<h3>表单提交</h3>
<form action="test/add" method="get">
<p>图书名称:<input type="text" name="bookName"></p>
<p>图书作者:<input type="text" name="bookAuthor"></p>
<p>图书价格:<input type="text" name="bookPrice"></p>
<p>出版时间:<input type="text" name="date"></p>
<p><input type="submit" value="提交"></p>
</form>
testController
@Controller
@RequestMapping("/test")
public class TestController {
// 表单提交的多个数据,在控制器方法中使用对象接收,但是提交的数据的key必须要与对象的属性名一致
@RequestMapping("add")
public String addBook(Book book){
System.out.println(book.getBookName());
return "tips";
}
}
六、SpringMVC自定义日期转换器
如果前端需要输入日期数据,在控制器中转换成Date对象,SpringMVC要求前端输入的日期格式必须为yyyy/MM/DD
如果甲方要求日期格式必须为指定的格式,而这个指定格式SpringMVC不接受,该如何处理呢?
- 自定义日期转换器:
6.1 创建自定义日期转换器
- 创建一个类实现Converter接口,泛型指定从什么类型转换为什么类型
- 实现Converter准换方法
public class MyDateConverter implements Converter<String, Date> {
SimpleDateFormat simpleFormatter=new SimpleDateFormat("yyyy年MM月dd日");
public Date convert(String s){
Date date = null;
try {
date = simpleFormatter.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
6.2 配置自定义转换器
<!-- 声明mvc使用注解驱动-->
<mvc:annotation-driven conversion-service="converterFactory"/>
<bean id="converterFactory" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.xie.utils.MyDateConverter"></bean>
</set>
</property>
</bean>
注意:要想要其他的格式,就可以多配置几个
七、文件上传
7.1 SpringMVC框架部署
- 基于Maven创建web工程
- 添加SpringMVC所需的依赖
- Spring-context aspects jdbc test web webmvc jackson
- 创建SpringMVC配置文件
- 在web.xml中配置SpringMVC的前端控制器
- 在web.xml中配置SpringMVC的编码过滤器
- 配置静态资源处理策略
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.29</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.29</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.29</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.29</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.29</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.29</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.1</version>
</dependency>
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 声明使用注解配置-->
<context:annotation-config/>
<!-- 声明Spring工厂注解的扫描范围-->
<context:component-scan base-package="com.xie"/>
<!-- 声明mvc使用注解驱动-->
<mvc:annotation-driven />
<!-- 配置静态资源处理策略-->
<mvc:resources mapping="/js/**" location="/js/"/>
</beans>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--编码过滤器-->
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
7.2 案例:添加图书,同时提交图书的封面图片
7.2.1 前端提交文件
- 准备工作
- book-add.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>添加图书信息</h1> <form action=""> <p>图书名称:<input type="text" name=""></p> <p>图书作者:<input type="text" name=""></p> <p>图书价格:<input type="text" name=""></p> <p>图书封面:<input type="file" name=""></p> <p><input type="submit" value="提交"></p> </form> </body> </html>- index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <table width="100%" height="700"> <tr> <td width="200" style="border-right: cornflowerblue 2px solid;background: rgba(255,0,0,0.1)"> <ul> <li><a href="book-add.jsp" target="mainFrame">添加图书</a></li> <li><a href="">图书列表</a></li> </ul> </td> <td> <iframe name="mainFrame" width="100%" height="700" frameborder="0"></iframe> </td> </tr> </table> </body> </html>- 新建包(controller)
@Controller @RequestMapping("/book") public class BookController { @RequestMapping("/add") public String addBook(){ System.out.println(".......add"); return "/tips.jsp"; } }- 新建tips.jsp
- 表单提交方式必须为post
- 表单 enctype属性 设置为 multipart/form-data
<h1>添加图书信息</h1>
<form action="book/add" method="post" enctype="multipart/form-data">
<p>图书名称:<input type="text" name="bookName"></p>
<p>图书作者:<input type="text" name="bookAuthor"></p>
<p>图书价格:<input type="text" name="bookPrice"></p>
<p>图书封面:<input type="file" name="bookFile"></p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
7.2.2 控制器接收数据和文件
SpringMVC处理上传文件需要借助CommonsMultipartResolver文件解析器
- 添加依赖:commons-io commons-fileupload
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.12.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
- 在spring-servlet.xml中配置文件解析器
<!-- 配置文件解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10240000"/>
<property name="maxInMemorySize" value="102400"/>
<property name="defaultEncoding" value="utf-8"/>
</bean>
- 控制器接收文件
- 在处理文件上传的方法中定义一个 MultipartFile 类型的对象,就可以接收图片了
注意:bookImages要和book-add.jsp里的名字一样@Controller @RequestMapping("/book") public class BookController { @RequestMapping("/add") public String addBook(Book book, MultipartFile bookImages){ System.out.println(".......add"); System.out.println(bookImages); return "/tips.jsp"; } }@Controller @RequestMapping("/book") public class BookController { @RequestMapping("/add") public String addBook(Book book, MultipartFile bookImages){ System.out.println(".......add"); // bookImages表示上传的图片 String originalFilename=bookImages.getOriginalFilename(); System.out.println(originalFilename);//会得到上传的图片的名字 return "/tips.jsp"; } } - 文件上传
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/add")
public String addBook(Book book, MultipartFile bookImages, HttpServletRequest request) throws IOException {
System.out.println(".......add");
// bookImages表示上传的图片
// 1.截取上传文件的后缀名,生成新的文件名
String originalFilename=bookImages.getOriginalFilename();
String ext=originalFilename.substring(originalFilename.lastIndexOf("."));//.jpg
String fileName=System.currentTimeMillis()+ext;
// 2:获取images目录在服务器的路径
String dir=request.getServletContext().getRealPath("images");
String savePath=dir+"/"+fileName;
// 3.保存文件
bookImages.transferTo(new File(savePath));
// 4。将图片的访问路径设置为book对象
book.setBookImg("images/"+fileName);
// 5. 调用service保存book到数据库
System.out.println(originalFilename);//会得到上传的图片的名字
return "/tips.jsp";
}
}
7.3 文件下载
7.3.1 文件下载-显示
- book-add.jsp
<h1>添加图书信息</h1>
<form action="book/add" method="post" enctype="multipart/form-data">
<p>图书名称:<input type="text" name="bookName"></p>
<p>图书作者:<input type="text" name="bookAuthor"></p>
<p>图书价格:<input type="text" name="bookPrice"></p>
<p>图书封面:<input type="file" name="bookImages"></p>
<p><input type="submit" value="提交"></p>
</form>
- index.jsp
<table width="100%" height="700">
<tr>
<td width="200" style="border-right: cornflowerblue 2px solid;background: rgba(255,0,0,0.1)">
<ul>
<li><a href="book-add.jsp" target="mainFrame">上传图片</a></li>
<li><a href="list.jsp" target="mainFrame">文件列表</a></li>
</ul>
</td>
<td>
<iframe name="mainFrame" width="100%" height="700" frameborder="0"></iframe>
</td>
</tr>
</table>
- list.jsp
<h4>文件列表</h4>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$.get("book/list",function (res){
console.log(res)
},"json",)
</script>
- Controller
@RequestMapping("/list")
@ResponseBody
public String[] listImages(HttpServletRequest request ){
// 1.从images目录下获取所有的图片信息
String dir = request.getServletContext().getRealPath("images");
File imgDri=new File(dir);
String[] fileNames = imgDri.list();
return fileNames;
}
<html>
<head>
<title>list</title>
</head>
<body>
<%-- 最新版本的BootStrap核心CSS文件--%>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
<%-- 最新的BootStrap核心JavaScript文件--%>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.min.js" integrity="sha384-ep+dxp/oz2RKF89ALMPGc7Z89QFa32C8Uv1A3TcEK8sMzXVysblLA3+eJWTzPJzT" crossorigin="anonymous"></script>
<h4>文件列表</h4>
<div class="row" id="container">
</div>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$.get("book/list",function (res){
for (var i=0;i<res.length;i++){
var fn=res[i];
var htmlStr="<div class='col-lg-2 col-md-3 col-sm-4 col-xs-6'><div class='thumbnail'><img src='images/"+fn+"' alt=''><div class='caption'><p><a href='' class='btn btn-primary' role='button'>下载</a></p></div></div></div>";
$("#container").append(htmlStr);
}
},"json",)
</script>
</body>
</html>
7.3.2 文件下载-实现
var htmlStr="<div class='col-lg-2 col-md-3 col-sm-4 col-xs-6'><div class='thumbnail'><img src='images/"+fn+"' alt=''><div class='caption'><p><a href='book/download?fname="+fn+"' class='btn btn-primary' role='button'>下载</a></p></div></div></div>";
@RequestMapping("/download")
public void downloadImg(String fname, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 从images目录找到当前文件
String dir = request.getServletContext().getRealPath("images");
String filePath=dir+"/"+fname;
FileInputStream fileInputStream = new FileInputStream(filePath);
response.setContentType("application/exe");
response.addHeader("content-Disposition","attachment;filename="+fname);
IOUtils.copy(fileInputStream,response.getOutputStream());
}
八、统一异常处理
在我们的应用系统运行的过程中,可能由于运行环境,用户操作,资源不足等各方面的原因导致系统出现异常(HTTP状态异常、Java异常Exception);如果系统出现了异常,这些异常将会通过浏览器呈现给用户,而这种异常的显示是没有必要的,因此我们可以在服务器进行特定的处理——当系统出现异常之后,呈现给用户一个统一的,可读的提示页面。
8.1 HTTP状态异常
HTTP Status 404 ;访问地址有误
HTTP Status 500是服务器有误或者网络异常
- 创建一个用于异常提示的页面:404.jsp
<html>
<head>
<title>404</title>
</head>
<body>
<h3>您的访问地址有误,请检查。。。。</h3>
</body>
</html>
- 在web.xml中进行配置
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
8.2 Java代码异常的统一处理
8.2.1基于Servlet-api的处理
- 创建异常提示页面:err.jsp
- 在web.xml中进行配置
<error-page>
<exception-type>java.lang.NumberFormatException</exception-type>
<location>/error.jsp</location>
</error-page>
8.2.2 SpringMVC处理
- 使用异常处理类进行统一处理
@ControllerAdvice
public class MyExceptionHandler {
//空指针异常
@ExceptionHandler(NullPointerException.class)
public String nullHandle(){
return "/error1.jsp";
}
//数据格式化异常
@ExceptionHandler(NumberFormatException.class)
public String formatHandler(){
return "/error.jsp";
}
}
九、拦截器
9.1 拦截器介绍
SpringMVC提供的拦截器就类似与Servlet-api中的过滤器,可以对控制器的请求进行拦截实现相关的预处理和后处理。
- 过滤器
- 是Servlet规范的一部分,所有web项目都可以使用
- 过滤器在web.xml配置(可以使用注解),能够拦截所有web请求
- 拦截器
- 是SpringMVC框架的实现,只有在SpringMVC框架中才能使用
- 拦截器在SpringMVC配置文件进行配置,不会拦截SpringMVC放行的资源(js/html/css..)
9.2 自定义拦截器
9.2.1 创建拦截器
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("。。。。。。预处理");
return false;//是否放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("。。。。。。后处理");
}
}
9.2.2 配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/book/query"/>
<mvc:mapping path="/student/**"/>
<mvc:exclude-mapping path="/student/add"/>
<bean class="com.xie.utils.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("。。。。。。预处理");
Enumeration<String> keys = request.getParameterNames();
while(keys.hasMoreElements()){
String key=keys.nextElement();
if("bookId".equals(key)){
return true;
}
}
response.setStatus(400);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
modelAndView.addObject("tips","这是通过拦截器的后处理器添加的数据");
System.out.println("。。。。。。后处理");
}
}
9.3 拦截器链
将多个拦截器按照一定的顺序构成一个拦截器链
再加一个拦截器并配置
这是拦截器1的配置在前面的时候,要是拦截器2在前面,则是“2112”