SSM学习笔记之SpringMVC
0x00 概述
本文主要涉及SpringMVC知识:SpringMVC部署,SpringMVC框架,请求流程,请求定制(日期和文件处理),统一异常处理,拦截流程。
0x01 BaseServlet
BaseServlet可以让一个Servlet处理多种不同的请求,不同的请求调用Servlet的不同方法;
随着项目需求复杂化,基于BaseServlet已经无法满足项目管理和开发的需求,这时候就需要引入web开发框架了;
0x02 SpringMVC概述
/** Spring MVC是由Spring官方提供的基于MVC设计理念的web框架 Spring MVC是基于Servlet封装的用于实现MVC控制的框架,实现前端和服务端的交互。 **/
2.1 SpringMVC的优势
/** 1. 严格遵守了MVC分层思想 2. 采用了松耦合、插件式结构;相比较于我们封装的BaseServlet以及其他的一些MVC框架来说更灵活、更具扩展性 3. SpringMVC是基于Spring的扩展、提供了一套完善的MVC注解 4. SpringMVC在数据绑定、视图解析都提供了多种处理方式,可灵活配置 5. SpringMVC对RESTful URL设计方法提供了良好的支持 **/
2.2 SpringMVC本质工作
/** 1. 接收并解析请求 2. 处理请求 3. 数据渲染,相应请求 **/
0x03 SpringMVC框架部署
3.1 基于Maven创建一个Web工程
3.2 添加SpringMVC依赖
/** spring-context spring-aspects spring-jdbc spring-web spring-webmvc spring-junit **/
<?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.springMVCDemo</groupId> <artifactId>springmvc-demo2</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <spring.version>5.2.13.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </project>
3.3 创建SpringMVC配置文件
/** 在resource目录下创建名为spring-servlet.xml文件 添加MVC命名空间 **/
<?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.springMVCDemo"/> <!-- 声明mvc使用注解驱动--> <mvc:annotation-driven/> </beans>
3.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>
0x04 SpringMVC框架使用
4.1 控制器Controller
在SpringMVC中,我们把接收用户请求,处理用户请求的类称之为Controller(控制器)
4.1.1 创建控制器
- 创建一个名为com.springMVCDemo.controllers的包(包需要在Spring注解扫描的范围内)
- 创建一个类(无需做任何的继承和实现)
- 在类上添加@Controller注解声明此类为SpringMVC的控制器
- 在类上添加@RequeMapping("/url")声明此控制器类的请求url
package com.springMVCDemo.controllers; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/book") public class BookController { }
4.1.2 在控制器类中定义处理请求的方法
- 在一个控制器类中可以定义多个方法处理不同的请求
- 在每个方法上添加@RequestMapping("/url")用于声明当前方法请求url
4.1.3 访问
- http://localhost:8081/springmvc_demo22_war_exploded/book/add
- http://localhost:8081/springmvc_demo22_war_exploded/book/list
4.2 静态资源配置
静态资源:就是项目中的HTML,CSS,JS,图片等
4.2.1 /* 和 / 区别
<!-- /* 拦截所有的HTTP请求,包括.jsp的请求,都做为控制器类的请求路径来处理--> <!-- / 拦截所有的HTTP请求,但不包括.jsp的请求,但不会放行静态资源请求(html/js/css/图片)--> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
4.2.2 静态资源放行配置
- 在SpringMVC的配置文件中,添加如下静态资源放行配置
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.springMVCDemo"/> <!-- 声明mvc使用注解驱动--> <mvc:annotation-driven/> ------------------------------------------------ <!--放行资源--> <mvc:resources mapping="/css/**" location="/css/"/> <mvc:resources mapping="/pages/**" location="/pages/"/> <mvc:resources mapping="/imgs/**" location="/imgs/"/> <mvc:resources mapping="/js/**" location="/js/"/> </beans>
4.3 前端提交数据到控制器
4.3.1 表单提交
表单提交:输入框需要提供name属性,springMVC控制器是通过name属性取值的
<%-- Created by IntelliJ IDEA. User: LT2 Date: 2022/3/05 Time: 21:33 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <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>
4.3.2 URL提交
URL提交: <a href="book/add?bookName=java">url提交</a>
4.3.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>
4.4 控制器中接收前端提交的数据
4.4.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="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>
- 控制器接收的数据
package com.springMVCDemo.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是一致的,则@RequetParam注解可以省略
@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); }
4.4.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 (){ console.log("进入方法"); var obj ={}; obj.bookName="Java"; obj.bookAuthor="张三"; obj.bookPrice=3.33; console.log("obj"); $.ajax({ url:"book/list", type:"post", headers:{ token:"Eqeqeq" }, // contentType:"application/json", // data:obj, success:function (res){ console.log(res); } }); console.log("结束"); }); </script>
4.4.3 请求体传值
- $.ajax封装请求体数据
$.ajax({
url:"book/add",
type:"post",
headers:{
这里请求头传值
},
contentType:"application/json",
data:{
这里放请求体数据
},
success:function (res){
console.log(res);
}
});
@RequestBody注解用于接收请求体传递的数据
- 前端
<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="pytone"; obj.bookAuthor="woshinidie"; obj.bookPrice=3.33; console.log("obj"); $.ajax({ url:"book/update", type:"post", // headers:{ // token:"Eqeqeq" // }, contentType:"application/json", data: JSON.stringify(obj), //data的值为json格式字符串, contentType:"application/json" success:function (res){ console.log(res); } }); console.log("结束"); }); </script>
@RequestBody将前端请求的JSON格式数据转换为Java对象,依赖jackson包
- 导入jackson的依赖
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.0</version> </dependency>
- 控制器
@RequestMapping("/update") public void update(@RequestBody Book book){ System.out.println("~~~~~book update"); try{ System.out.println(book); } catch (Exception e) { e.printStackTrace(); } } ---------------------------------- // @RequestMapping("/update") // public void update(HttpServletRequest request){ // System.out.println("~~~~~book update"); // try{ //// Servlet的处理方法: 使用request 的输入流接收请求体数据 // ServletInputStream is = request.getInputStream(); // StringBuffer stringBuffer = new StringBuffer(); // byte[] bs = new byte[100]; // int len = -1; // while ((len = is.read(bs)) != -1){ // String s = new String(bs,0,len); // stringBuffer.append(s); // } // System.out.println(stringBuffer.toString()); // // } catch (IOException e) { // e.printStackTrace(); // } // }
4.5 控制器响应前端请求
4.5.1 控制器响应同步请求
同步请求: from,超链接
- 处理同步请求的方法返回类型定义为String或者ModelAndView,以实现页面的跳转
- 返回类型为String
转发
@RequestMapping("/add") public String 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); //如何挑战到指定的页面呢? 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"); System.out.println(a); System.out.println(b); System.out.println(c); //如何挑战到指定的页面呢? 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"); System.out.println(a); System.out.println(b); System.out.println(c); //如何挑战到指定的页面呢? 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"); System.out.println(a); System.out.println(b); System.out.println(c); //如何挑战到指定的页面呢? ModelAndView modelAndView = new ModelAndView("redirect/tips.jsp"); return modelAndView; }
4.5.2 控制器应异步请求
异步请求:ajax请求
使用response中的输出流进行响应
/** 控制器方法的返回类型为void 控制器方法添加HttpServletResponse response 参数 在方法中通过response 获取输出流,使用流响应ajax请求 **/
@RequestMapping("/update") public void update(@RequestBody Book book, HttpServletResponse response){ System.out.println("~~~~~book update"); try{ System.out.println(book); //使用ObjectMapper将对象转换成json格式字符串 String s = new ObjectMapper().writeValueAsString(book); response.setCharacterEncoding("utf-8"); response.setContentType("application/json"); PrintWriter out = response.getWriter(); out.println(); out.close(); } catch (Exception e) { e.printStackTrace(); } }
直接在控制器方法返回相应的对象
- 控制器方法的返回类型设置为响应给ajax请求的对象类型
- 在控制器方法前添加一个@ResponseBody注解,将返回的对象转换成json格式返回给ajax请求
- 如果一个控制器类中的所有方法都是响应ajax请求,则可以直接在控制器类前添加@ResponseBody注解
@RequestMapping("/update") @ResponseBody public List<Book> update(){ System.out.println("~~~~~book update"); ArrayList<Book> books = new ArrayList<Book>(); books.add(new Book(1,"我朝","嘿嘿",2.33)); books.add(new Book(2,"到店","顶顶",2.33)); return books; }
4.5.3 控制器响应同步请求的数据传递
对于同步请求的转发响应,我们可以传递参数到转发的页面
- 返回类型为String:
//1:在控制器方法中定义一个Model类型的参数 //2:在return页面之前,向model中添加键值对,添加的键值对就会被传递到转发的页面 @RequestMapping("/add1") public String add1(@RequestParam("name") String a, @RequestParam("author") String b, @RequestParam("price") double c, Model model){ model.addAttribute("key1","value1"); model.addAttribute("key1",new Book(1,"Java","老张",2.33)); return "/tips.jsp"; } //除了使用Model对象传值外还可以直接使用HttpServletRequest对象 @RequestMapping("/add1") public String add1(@RequestParam("name") String a, @RequestParam("author") String b, @RequestParam("price") double c, HttpServletRequest request){ request.addAttribute("key1","value1"); request.addAttribute("key1",new Book(1,"Java","老张",2.33)); return "/tips.jsp"; }
- 返回类型为ModelAndView
@RequestMapping("/add2") public ModelAndView add2(@RequestParam("name") String a, @RequestParam("author") String b, @RequestParam("price") double c){ System.out.println("~~~~~book add"); ModelAndView modelAndView = new ModelAndView("/tips.jsp"); modelAndView.addObject("key1","value"); modelAndView.addObject("key2",new Book(1,"Java","老张",2.35)); return modelAndView; }
4.6 解决中文乱码问题
4.6.1 前端编码
- JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
- HTML页面
<meta charset="UTF-8">
4.6.2 服务器编码
- tomcat/conf/server.xml
<Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/>
4.6.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>
0x05 SpringMVC的请求处理流程
5.1 SpringMVC的请求处理流程
SpringMVC通过前端控制器DispatcherServlet拦截并处理用户请求
5.2 SpringMVC的核心组件
- DispatcherServlet前端控制器,总控制器
- 作用:接收请求,协同各个组件工作,响应请求
- HandlerMapping处理器映射
- 作用:负责根据用户请求的URL找到对应的Handler
- 可以自定义配置SpringMVC提供多个处理器映射的实现,可以根据需要进行配置
- HandlerAdapter处理器适配器
- 作用:根据HandlerMapping解析用户请求后产生的调用链,通过适配器模式完成Handler的调用
- Handler控制器
- 由开发人员根据业务的需求进行开发
- 作用:处理请求
- ModelAndView视图模型
- 作用:用于封装处理器返回的数据以及响应的视图
- ModelAndView = Model + View
- ViewResolver视图解析器
- 作用:对ModelAndView进行解析
- 可以自定义配置SpringMVC提供多个视图解析器的实现,可以根据需要进行配置
- View视图
- 作用:完成数据渲染
5.3 处理器映射器
不同的处理器映射器对URL处理的方式也不相同,使用对应的处理器映射器之后,我们的前端请求规则也需要发生响应的变化
SpringMVC提供的处理器映射器:
1. BeanNameUrlHandlerMappering 根据控制器的Id访问控制器
2. SimpleUrlHandlerMapping 根据控制器配置的URL访问(默认配置)
配置处理器映射器:
- 在SpringMVC的配置文件中通过Bean标签声明处理器映射器
- 配置BeanNameUrlHandlerMapping
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
- 配置SimpleHandlerMapping
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> //这里也可以通过注解的方式在controller上进行配置@RequestMapping("/aaa") <prop key="/aaa">bookController</prop> <prop key="/bbb">studentController</prop> </props> </property> </bean>
5.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>
-
- 配置视图解析器
spring-servlet.xml
<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>
-
- InternalResourceViewResolver
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean>
0x06 SpringMVC中的日期格式处理
6.1 在控制器中使用对象接收数据
- 前端
<form action="test/add" method="post"> <p>图书名称:<input type="text" name="bookId"></p> <p>图书作者:<input type="text" name="bookName"></p> <p>图书价格:<input type="text" name="bookPrice"></p> <p>图书出版时间:<input type="text" name="publish"></p> <p><input type="submit" value="提交"></p> </form>
- 后端
package com.springMVCDemo.beans; import java.util.Date; public class Book { private int bookId; private String bookName; private String bookAuthor; private double bookPrice; private Date publish; public Book(int bookId, String bookName, String bookAuthor, double bookPrice) { this.bookId = bookId; this.bookName = bookName; this.bookAuthor = bookAuthor; this.bookPrice = bookPrice; } @Override public String toString() { return "Book{" + "bookId=" + bookId + ", bookName='" + bookName + '\'' + ", bookAuthor='" + bookAuthor + '\'' + ", bookPrice=" + bookPrice + ", publish=" + publish + '}'; } 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 getPublish() { return publish; } public void setPublish(Date publish) { this.publish = publish; } public Book(int bookId, String bookName, String bookAuthor, double bookPrice, Date publish) { this.bookId = bookId; this.bookName = bookName; this.bookAuthor = bookAuthor; this.bookPrice = bookPrice; this.publish = publish; } public Book() { } } -------------------------- package com.springMVCDemo.controllers; import com.springMVCDemo.beans.Book; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import java.util.Date; @Controller @RequestMapping("/test") public class TestController { @RequestMapping("/add") //表单提交的多个参数,在控制器方法中可以使用对象接收, //但是提交的数据的key必须要与对象的属性一致 public String addBook(Book book){ System.out.println(book); return "tips"; } }
- spring-servlet.xml
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean>
6.2 日期格式处理
如果前端需要输入日期数据,在控制器中转换成Date对象,SpringMVC要求前端输入的日期格式必须为yyyy/MM/DD
如果甲方要求日期格式必须为指定的格式,而这个指定格式SpringMVC不接受,该如何处理?
自定义日期转换器:
6.2.1 创建自定义日期转换器
package com.springMVCDemo.utils; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /* * 1:创建一个类实现Converter接口,泛型指定从什么类型转换为什么类型 * 2:实现Converter准换方法 * */ public class MyDateConverter implements Converter<String,Date> { SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日"); public Date convert(String s) { Date date = null; try { date = sdf.parse(s); } catch (ParseException e) { e.printStackTrace(); } return date; } }
6.2.2 配置自定义转换器
<?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.springMVCDemo"/> -------------------------------------------------------------------------------- <!-- 声明mvc使用注解驱动--> <mvc:annotation-driven conversion-service="converterFactory"/> ------------------------------------------------------------------------ <!--放行资源--> <mvc:resources mapping="/css/**" location="/css/"/> <mvc:resources mapping="/pages/**" location="/pages/"/> <mvc:resources mapping="/imgs/**" location="/imgs/"/> <mvc:resources mapping="/js/**" location="/js/"/> <!--HandlerMapping--> <!-- <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>--> <!-- <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">--> <!-- <property name="mappings">--> <!-- <props>--> <!-- <prop key="/aaa">bookController</prop>--> <!-- <prop key="/bbb">studentController</prop>--> <!-- </props>--> <!-- </property>--> <!-- </bean>--> <!-- <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>--> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"/> <property name="suffix" value=".jsp"/> </bean> ----------------------------------------------------------- <!--自定义转换器--> <bean id="converterFactory" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <!-- //这里可以配置多个--> <bean class="com.liguoqing.utils.MyDateConverter"/> </set> </property> </bean> ---------------------------------------------- </beans>
//自己定义一个转换类 //MyDateConverter.class package com.springMVCDemo.utils; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /* * 1:创建一个类实现Converter接口,泛型指定从什么类型转换为什么类型 * 2:实现Converter准换方法 * */ public class MyDateConverter implements Converter<String,Date> { SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日"); public Date convert(String s) { Date date = null; try { date = sdf.parse(s); } catch (ParseException e) { e.printStackTrace(); } return date; } }
0x07 SpringMVC中的文件上传和下载
7.1 SpringMVC框架部署
- 基于Maven创建web工程
- 添加SpringMVC所需的依赖
- Spring-context
- aspects
- jdbc
- test
- web
- webmvc
- jackson
- 创建SpringMVC配置文件
- 在web.xml中配置SpringMVC的前端控制器
- 在web.xml中配置SpringMVC的编码过滤器
- 配置静态资源处理策略
7.2 文件上传
案例:添加图书,同时提交图书的封面图片
7.2.1 前端提交文件
表单提交方式必须为post
表单enctype属性,设置为multipart/form-data
<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="ImgFile"></p> <p>提交:<input type="submit" name="提交"></p> </form>
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.4</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
- 在spring-servlet.xml中配置文件解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--一些属性,自己百度--> <property name="maxUploadSize" value="10240000"/> <property name="maxInMemorySize" value="10240"/> <property name="defaultEncoding" value="utf-8"/> </bean>
- 在控制器接收文件
- 在处理文件上传的方法中定义一个MultipartFile类型的对象,就可以接收图片了
package com.springMVCDemo.controller; import com.springMVCDemo.beans.Book; import org.springframework.http.HttpRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; @Controller @RequestMapping("/book") public class BookController { @RequestMapping("/add") public String addBook(Book book, MultipartFile ImgFile, HttpServletRequest request) throws IOException { System.out.println("~~~~addBook"); System.out.println(book); System.out.println(ImgFile); //ImgFile表示上传的图片 //1:截取上传文件的后缀名,生成新的文件名 String originalFilename = ImgFile.getOriginalFilename(); String ext = originalFilename.substring( originalFilename.lastIndexOf(".") );// .jpg String filename = System.currentTimeMillis()+ext; //2:获取imgs目录在服务器的路径 String dir = request.getServletContext().getRealPath("imgs"); String savePath = dir + "/" + filename;//存储路径(C:/asd/ad/asdf/img.jpg) //3:保存文件 ImgFile.transferTo(new File(savePath)); //4:将图片的访问路径设置到对象 book.setBookImg("imgs/"+ filename); //5:调用service保存book到数据库 return "/tips.jsp"; } }
7.3 文件下载
7.3.1 文件下载流程
@RequestMapping("/list") @ResponseBody public String[] listImgs(HttpServletRequest request){ //1:从imgs目录下获取所有的图片信息 String dir = request.getServletContext().getRealPath("imgs"); File imgDir = new File(dir); String[] filenames = imgDir.list(); return filenames; }
7.3.2 实现文件下载
0x08 SpringMVC中的统一异常处理
在我们的应用系统运行的过程中,可能由于运行环境,用户操作,资源不足等各个方面的原因导致系统出现异常
(HTTP状态异常,Java异常Exception);如果系统出现了异常,这些异常将会通过浏览器呈现给用户,而这种异常的显示是没有必要的,
因此我们可以在服务器进行特定的处理--当系统出现异常之后,呈现给用户一个统一的可读的提示页面
8.1 HTTP异常状态统一处理
HTTP Status 404
- 创建一个用于异常提示的页面: 404.jsp
- 在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.NullPointerException</exception-type> <location>/err.jsp</location> </error-page>
8.2.2 SpringMVC处理
- 使用异常处理类进行统一处理
0x09 SpringMVC的拦截器
9.1 拦截器介绍
SpringMVC提供的拦截器类似与Servlet-api中的过滤器,可以对控制器的请求进行拦截实现相关的预处理和后处理
- 过滤器
- 是Servlet规范的一部分,所有web项目都可以使用
- 过滤器在web.xml配置(可以使用注解),能够拦截所有web请求
- 拦截器
- 是SpringMVC框架的实现,只有在SpringMVC框架中才能使用
- 拦截器在SpringMVC配置文件进行配置,不会拦截SpringMVC放行的资源(js/html/css....)
9.2 自定义拦截器
9.2.1 创建拦截器
9.2.2 配置拦截器
9.3 拦截器链
将多个拦截器按照一定的顺序构成一个拦截器链