SpringMVC知识点记录
1. SpringMVC简介
2. 入门案例
文件结构
依赖
<?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.atguigu</groupId>
<artifactId>spring_mvc_helloworld</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式为war-->
<packaging>war</packaging>
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
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的前端控制器,对浏览器发送的请求进行处理
SpringMVC的配置文件的默认位置和名称:
位置:WEB-INF下
名称:<servlet-name>-servlet.xml,当前配置下的配置文件名为SpringMVC-servlet.xml
-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--设置SpringMVC配置文件的位置和名称-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
<!--将DispatcherServlet的初始化时间提前到了服务器启动时-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!--
url-pattern:
/:匹配浏览器向服务器发送的所有请求,不包括.jsp
/*:匹配浏览器向服务器发送的所有请求,包括.jsp
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
SpringMVC配置文件
<?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"
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">
<!--扫描控制层组件-->
<context:component-scan base-package="com.atguigu.controller"/>
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--/WEB-INF/templates/index.html-->
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
请求控制器类
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
//@RequestMapping注解:处理请求和控制器方法之间的映射关系
//@RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径
@RequestMapping("/")
public String protal(){
//将逻辑视图返回
return "index";
}
@RequestMapping("/hello")
public String hello(){
return "success";
}
}
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>xixi</h3>
<a th:href="@{/hello}">测试SpringMVC</a>
</body>
</html>
sucess.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>成功</h1>
</body>
</html>
3. @RequestMapping注解
3.1 @RequestMapping注解的功能
@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。
3.2 @RequestMapping注解的位置
@RequestMapping注解标识的位置:
1、标识一个类:设置映射请求的请求路径的初始信息
2、标识一个方法:设置映射请求的请求路径的具体信息
/**
* @RequestMapping注解标识的位置:
* 1、标识一个类:设置映射请求的请求路径的初始信息
* 2、标识一个方法:设置映射请求的请求路径的具体信息
*/
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
//此时控制器方法所匹配的请求的请求路径为/test/hello
@RequestMapping("/hello")
public String helloworld(){
return "success";
}
}
3.3 @RequestMapping注解的value属性
@RequestMapping注解values属性:
作用: 通过请求的请求路径匹配请求;
value属性是数组类型,即若当前浏览器所发送请求的请求路径匹配value属性中的任何一个值,则当前请求就会被注解所标识的方法进行处理。
/**
* @RequestMapping注解values属性:
* 作用:通过请求的请求路径匹配请求;
* value属性是数组类型,即若当前浏览器所发送请求的请求路径匹配value属性中的任何一个值,则当前请求就会被注解所标识的方法进行处理。
*/
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
//此时控制器方法所匹配的请求的请求路径为/test/hello或者/test/abc
@RequestMapping({"/hello","/abc"})
public String helloworld(){
return "success";
}
}
3.4 @RequestMapping注解的method属性
@RequestMapping注解的method属性
作用: 通过请求的请求方式匹配请求;
method属性时RequestMethod类型的数组,即若当前浏览器所发送请求的请求方式匹配method属性中的任何一种请求方式,则前请求就会被注解所标识的方法进行处理。
常见的异常报错
若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但是请求方式不匹配,则此时页面报错405
注意:
除了表单提交、Ajax请求等是post请求之外,基本都是get请求
/**
* @RequestMapping注解的method属性
* 作用:通过请求的请求方式匹配请求;
* method属性时RequestMethod类型的数组,即若当前浏览器所发送请求的请求方式匹配method属性中的任何一种请求方式,则前请求就会被注解所标识的方法进行处理。
* 若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但是请求方式不匹配,则此时页面报错405
*/
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
//此时控制器方法所匹配的请求的请求路径为/test/hello或者/test/abc
@RequestMapping(value = {"/hello","/abc"},method = RequestMethod.POST)
public String helloworld(){
return "success";
}
}
在@RequestMapping的基础上,结合请求方式的一些派生注解:
@GetMapping、@PostMapping、@DeleteMapping、@PutMapping
3.5 @RequestMapping注解的params属性(了解)
/**
* @RequestMapping注解的params属性:
* 作用:通过请求的请求参数匹配请求,即浏览器发送的请求的请求参数必须满足params属性的设置;
*
* params可以使用四种表达式:
* "param":当前请求的请求参数中必须携带param参数;
* "!param":当前请求的请求参数中不能携带param参数;
* "param=value":当前请求的请求参数中必须携带param参数,且值为value;
* "param!=value":当前请求的请求参数中可以不携带param参数,如果携带了param参数,必须不能等于value。(简言之,不能接待value的param参数);
*
* 参数不符合时,报错:400-Parameter conditions "username" not met for actual request parameters:
*/
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
//此时控制器方法所匹配的请求的请求路径为/test/hello或者/test/abc
@RequestMapping(value = {"/hello", "/abc"}, method = {RequestMethod.POST, RequestMethod.GET},params ={"username","!password","age=20","sex=女"}
)
public String helloworld() {
return "success";
}
}
3.6 @RequestMapping注解的headers属性(了解)
/**
* @RequestMapping注解的headers属性:
* "header":要求请求映射所匹配的请求必须携带header请求头信息
* "!header":要求请求映射所匹配的请求必须不能携带header请求头信息
* "header=value":要求请求映射所匹配的请求必须携带header请求头信息且header=value
* "header!=value":要求请求映射所匹配的请求必须携带header请求头信息且header!=value
*
* headers = {"referer"} 请求头中必须含有referer属性,代表着必须有来源页面,代表着不能靠手动输入地址来访问二级以下的页面;
*
* 注意:若浏览器所发送的请求的请求路径和@RequestMapping注解value属性匹配,但是请求头不匹配,则报错404
*/
@Controller
@RequestMapping("/test")
public class TestRequestMappingController {
//此时控制器方法所匹配的请求的请求路径为/test/hello或者/test/abc
@RequestMapping(
value = {"/hello", "/abc"},
method = {RequestMethod.POST, RequestMethod.GET},
headers = {"referer"}
)
public String helloworld() {
return "success";
}
}
3.7 SpringMVC支持ant风格的路径
?:表示任意的单个字符
*
:表示任意的0个或多个字符
**
:表示任意层数的任意目录
注意:在使用时,只能使用//xxx的方式
// a?a :a和a的中间可以表示任意一个字符,例如/test/aba/ant。但是注意有些特殊字符(?)除外。
//@RequestMapping("/a?a/ant")
// a*a :a和a的中间可以表示任意一个字符,例如/test/abba/ant。但是注意有些特殊字符(?和/)除外。
//@RequestMapping("/a*a/ant")
// 代表多层目录 例如:/test/abba/abba/ant
@RequestMapping("/**/ant")
public String testAnt(){
return "success";
}
3.8 SpringMVC支持路径中的占位符(重点)
/**
* SpringMVC使用路径中的占位符
* 传统方式:/deleteUser?id=1
* rest方式:/user/delete/1
* 需要在@RequestMapping注解的value属性中所设置的路径中,使用{xxx}的方式表示路径中的数据,
* 再通过@PathVariable注解,将占位符所标识的值和控制器方法的形参进行绑定
*/
@RequestMapping("/rest/{id}/{username}")
public String testRest(@PathVariable("id") Integer id,@PathVariable("username") String username){
System.out.println("id="+id+";username="+username);//最终输出结果:id=1;username=admin
return "success";
}
4. SpringMVC获取请求参数
4.1 通过ServletAPI获取请求参数
/**
* 1、通过ServletAPI获取请求参数
* 只需要在控制器方法的形参位置设置HttpServletRequest类型的形参,就可以在控制器方法中使用request对象获取请求参数。
* @param request
* @return
*/
@RequestMapping("/param/servletAPI")
public String getParamByServletAPI(HttpServletRequest request){
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username="+username+";password="+password);//输出结果:username=admin;password=123
return "success";
}
4.2 通过控制器方法的形参获取请求参数
/**
* 2、通过控制器方法的形参获取
* 只需要在控制器方法的形参位置设置一个形参,形参的名字要和请求参数的名字一致即可。
* @param username
* @param password
* @return
*/
//通过控制器方法的形参获取请求参数
@RequestMapping("/param1")
public String getParam1(String username,String password){
System.out.println("username="+username+";password="+password);//输出结果:username=admin;password=123
return "success";
}
4.3 @RequestParam注解
/**
* 3、@RequestParam注解
* 通过控制器方法的形参获取请求参数,并且使用@RequestParam注解解决形参和前端请求参数名字不一致的情况
* @RequestParam注解:将请求参数和控制器方法的形参进行绑定;
* 三个属性:
* value = "userName" :设置和形参绑定的请求参数的名字;
* required = false :设置是否必须传输value所对应的请求参数,默认值为true;
* 注意:若设置了required = true,而没有传输则会报错 400;
* defaultValue = "User1" :设置当没有传输value所对应的请求参数时,为形参设置默认值,前提是需要设置required = false;
*
*/
@RequestMapping("/param2")
public String getParam2(@RequestParam(value = "userName") String username, @RequestParam("passWord") String password){
System.out.println("username="+username+";password="+password);//输出结果:username=admin;password=123
return "success";
}
4.4 @RequestHeader注解 和 @CookieValue注解
@RequestHeader是将请求头信息和控制器方法的形参创建映射关系
@RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
@CookieValue是将cookie数据和控制器方法的形参创建映射关系
@CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam
/**
* 3、@RequestParam注解
* 通过控制器方法的形参获取请求参数,并且使用@RequestParam注解解决形参和前端请求参数名字不一致的情况
* @RequestParam注解:将请求参数和控制器方法的形参进行绑定;
* 三个属性:
* value = "userName" :设置和形参绑定的请求参数的名字;
* required = false :设置是否必须传输value所对应的请求参数,默认值为true;
* 注意:若设置了required = true,而没有传输则会报错 400;
* defaultValue = "User1" :设置当没有传输value所对应的请求参数时,为形参设置默认值,前提是需要设置required = false;
* 4、@RequestHeader注解:将请求头信息和控制器方法的形参绑定
* 5、@CookieValue注解:将cookie数据和控制器方法的形参绑定
*/
@RequestMapping("/param2")
public String getParam2(
@RequestParam(value = "userName") String username,
@RequestParam("passWord") String password,
@RequestHeader("referer") String referer,
@CookieValue("JSESSIONID") String jsessionId
){
System.out.println("username="+username+";password="+password);//输出结果:username=admin;password=123
System.out.println("referer="+referer);//输出结果:referer=http://localhost:8080/spring_mvc_demo_war_exploded/
System.out.println("cookie="+jsessionId);//输出结果:cookie=61A430AE2B0EC2959F2C0C0B3825A8B5
return "success";
}
4.5 通过POJO获取请求参数
/**
* 通过控制器方法的实体类型的形参获取请求参数:
* 需要在控制器方法的形参位置设置实体类类型的形参,要保证实体类中的属性的属性名和请求参数的名字一致,可以通过实体类类型的形参获取请求参数。
* @param user
* @return
*/
@RequestMapping("param/pojo")
public String getParamByPojo(User user){
System.out.println(user); //输出结果:User{id=null, username='admin', password='123'}
return "success";
}
4.6 解决获取请求参数的乱码问题
tomcat8以后get请求不需要解决乱码问题,post请求需要自己来解决;
web.xml中加入配置(SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效)
<!--配置Spring的编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
5. 域对象共享数据
5.1 使用ServletAPI想request域对象共享数据(不常用)
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request){
request.setAttribute("testScope", "hello,servletAPI");
return "success";
}
5.2 使用ModelAndView向request域对象共享数据
/**
* 向域对象共享数据:
* 1、通过ModelAndView向请求域共享数据
* 使用ModelAndView时,可以使用其Model功能向请求域共享数据
* 使用View功能设置逻辑视图,但是控制器方法一定要将ModelAndView作为方法的返回值
* @return
*/
@RequestMapping("/test/mav")
public ModelAndView testMAV(){
/**
* ModelAndView包含Model和View的功能:
* Model:向请求域中共享数据;
* View:设置逻辑视图实现页面跳转;
*/
ModelAndView mav = new ModelAndView();
//向请求域中共享数据
mav.addObject("testRequestScope", "hello,ModelAndView");
//设置逻辑视图
mav.setViewName("success");
return mav;
}
success.html
<p th:text="${testRequestScope}"></p>
运行结果
5.3 使用Model向request域对象共享数据
/**
* 使用Model向请求域中共享数据
* @param model
* @return
*/
@RequestMapping("/test/model")
public String testModel(Model model){
model.addAttribute("testRequestScope","hello,Model");
return "success";
}
运行结果
5.4 使用ModelMap向request域对象共享数据
/**
* 使用ModelMap向请求域中共享数据
* @param modelMap
* @return
*/
@RequestMapping("/test/modelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("testRequestScope","hello,ModelMap");
return "success";
}
运行结果
5.5 使用map向request域对象共享数据
/**
* 使用Map向请求域中共享数据
* @param map
* @return
*/
@RequestMapping("/test/map")
public String testMap(Map<String,Object> map){
map.put("testRequestScope","hello,Map");
return "success";
}
运行结果
5.6 Model、ModelMap、Map的关系
Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的
public interface Model{}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}
5.7 向session域共享数据
/**
* 向session域共享数据
* @param session
* @return
*/
@RequestMapping("/test/session")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope","hello,session");
return "success";
}
success.html
<p th:text="${session.testSessionScope}"></p><br>
运行结果
5.8 向application域共享数据
/**
* 向application域共享数据
* @param session
* @return
*/
@RequestMapping("/test/application")
public String testApplication(HttpSession session){
ServletContext servletContext = session.getServletContext();
servletContext.setAttribute("testApplicationScope","hello,application");
return "success";
}
success.html
<p th:text="${application.testApplicationScope}"></p><br>
运行结果
6.SpringMVC的视图
6.1 ThymeleafView
当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置
的视图解析器解析,视图名称拼接视图前缀和视图
后缀所得到的最终路径,会通过转发的方式实现跳转
@RequestMapping("/test/view/thymeleaf")
public String testThymeleafView(){
return "success";
}
6.2 转发视图 InternalResourceView
/**
* 转发视图:InternalResourceView
* @return
*/
@RequestMapping("/test/view/forward")
public String testInternalResourceView(){
return "forward:/test/model";
}
运行结果
6.3 重定向视图 RedirectView
/**
* 重定向视图:RedirectView
* @return
*/
@RequestMapping("/test/view/redirect")
public String testRedirectView(){
return "redirect:/test/model";
}
运行结果
6.4 视图控制器 view-controller
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示
SpringMVC.xml
<!--开启mvc的注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--
视图控制器:为当前的请求直接设置视图名称实现页面跳转
若设置视图控制器,则只有视图控制器所设置的请求会被处理,其他的请求将全部404,
此时必须再配置一个标签来开启mvc的注解驱动(<mvc:annotation-driven></mvc:annotation-driven>)
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
7. RESTful
7.1 RESTful简介
REST:Representational State Transfer,表现层资源状态转移。
7.2 RESTful的实现
GET 用来获取资源;
POST 用来新建资源;
PUT 用来更新资源;
DELETE用来删除资源;
REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数。
而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
操作 | 传统方式 | REST风格 |
---|---|---|
查询操作 | getUserById?id=1 | user/1 (get请求方式) |
保存操作 | saveUser | user (post请求方式) |
删除操作 | deleteUser?id=1 | user/1 (delete请求方式) |
更新操作 | updateUser | user (put请求方式) |
查询操作
/**
* 查询所有用户信息
* @return
*/
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getAllUser(){
System.out.println("查询所有用户信息 --> /user --> get");
return "success";
}
/**
* 根据id查询用户信息
* @param id
* @return
*/
@RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
public String getUserById(@PathVariable("id") Integer id){
System.out.println("根据id查询用户信息 --> /user/"+id+" --> get");
return "success";
}
添加操作
/**
* 添加用户信息
* @return
*/
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String insertUser(){
System.out.println("添加用户信息--> /user --> post请求");
return "success";
}
7.3 HiddenHttpMethodFilter
修改操作和删除操作
/**
* 修改用户信息
* 注意:浏览器目前只能发送get和post请求,若要发送put和delete请求,需要在web.xml中配置一个过滤器HiddenHttpMethodFilter
* 配置了过滤器之后,发送的请求需要满足两个条件,才能将请求方式转换为put和delete
* 1、当前请求的请求方式必须为post
* 2、当前请求必须传送请求参数_method,_method的值才是最终的请求方式
* @return
*/
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String updatetUser(){
System.out.println("修改用户信息--> /user --> put请求");
return "success";
}
/**
* 删除用户信息
* @param id
* @return
*/
@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)
public String deleteUser(@PathVariable("id") Integer id){
System.out.println("删除用户信息--> /user/"+id+" --> delete请求");
return "success";
}
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">
<!--配置Spring的编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--设置处理请求方式的过滤器-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--设置SpringMVC的前端控制器-->
<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:SpringMVC.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>
</web-app>
index.html中设置请求方式
<form th:action="@{/user}" method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="修改用户信息">
</form>
<form th:action="@{/user/5}" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="删除用户信息">
</form>
8. RESTful 案例
8.1 准备工作
实体类
package com.atguigu.pojo;
public class Employee {
private Integer id;
private String lastName;
private String email;
//1 male, 0 female
private Integer gender;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Employee(Integer id, String lastName, String email, Integer
gender) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
}
public Employee() {
}
}
DAO
package com.atguigu.dao;
import com.atguigu.pojo.Employee;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@Repository
public class EmployeeDao {
private static Map<Integer, Employee> employees = null;
static{
employees = new HashMap<Integer, Employee>();
employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1));
employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1));
employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0));
employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0));
employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1));
}
private static Integer initId = 1006;
public void save(Employee employee){
if(employee.getId() == null){
employee.setId(initId++);
}
employees.put(employee.getId(), employee);}
public Collection<Employee> getAll(){
return employees.values();
}
public Employee get(Integer id){
return employees.get(id);
}
public void delete(Integer id){
employees.remove(id);
}
}
控制类 EmployeeController.java
package com.atguigu.controller;
import org.springframework.stereotype.Controller;
@Controller
public class EmployeeController {
@Autowired
private EmployeeDao employeeDao;
}
8.2 功能清单
功能 | URL地址 | 请求方式 |
---|---|---|
访问首页 | / | GET |
查询全部数据 | /employee | GET |
删除 | /employee/2 | DELETE |
跳转到添加数据页面 | /toAdd | GET |
执行保存 | /employee | POST |
跳转到更新数据页面 | /employee/2 | GET |
执行更新 | /employee | PUT |
8.3 具体功能:查询功能
8.3.1 查询功能
控制类 EmployeeController.java
/**
* 查询所有的员工信息
* @param model
* @return
*/
@RequestMapping(value = "/employee",method = RequestMethod.GET)
public String getAllEmployee(Model model){
//获取所有的员工信息
Collection<Employee> employees = employeeDao.getAll();
//将所有的员工信息在请求域中共享
model.addAttribute("employees",employees);
//跳转到列表页面
return "employee_list";
}
employee_list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>employee_list</title>
<link rel="stylesheet" th:href="@{/static/css/index_work.css}">
</head>
<body>
<table>
<tr>
<th colspan="5">employee list</th>
</tr>
<tr>
<td>id</td>
<td>lastName</td>
<td>email</td>
<td>gender</td>
<td>options(<a th:href="@{/to/add}">add</a> )</td>
</tr>
<tr th:each="employee : ${employees}">
<td th:text="${employee.id}"></td>
<td th:text="${employee.lastName}"></td>
<td th:text="${employee.email}"></td>
<td th:text="${employee.gender}"></td>
<td>
<a href="">delete</a>
<a href="">update</a>
</td>
</tr>
</table>
</body>
</html>
运行结果
8.3.2 解决静态资源问题
在SpringMVC.xml中配置DefaultServlet来处理静态资源
<mvc:default-servlet-handler></mvc:default-servlet-handler>
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.atguigu"/>
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--/WEB-INF/templates/index.html-->
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!--
配置默认的servlet处理静态资源
当前工程的web.xml配置的前端控制器DispatcherServlet的<url-pattern>/</url-pattern>是斜线
tomcat的web.xml配置的默认的DefaultServlet的<url-pattern>/</url-pattern>也是斜线
此时,浏览器发送的请求冲突了,会优先被DispatcherServlet进行处理,但是DispatcherServlet无法处理静态资源
若配置了<mvc:default-servlet-handler></mvc:default-servlet-handler>,此时浏览器发送的所有请求都会被DefaultServlet来处理。
若配置了<mvc:default-servlet-handler></mvc:default-servlet-handler>和<mvc:annotation-driven></mvc:annotation-driven>,
则浏览器发送的请求会先被DispatcherServlet来处理,无法处理再交给DefaultServlet来处理。
-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--开启mvc的注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--
视图控制器:为当前的请求直接设置视图名称实现页面跳转
若设置视图控制器,则只有视图控制器所设置的请求会被处理,其他的请求将全部404,
此时必须再配置一个标签来开启mvc的注解驱动(<mvc:annotation-driven></mvc:annotation-driven>)
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
</beans>
8.4 具体功能:添加功能
SpringMVC.xml
视图控制器中添加视图
<mvc:view-controller path="/to/add" view-name="employee_add"></mvc:view-controller>
<?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.atguigu"/>
<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver"
class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!--/WEB-INF/templates/index.html-->
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!--
配置默认的servlet处理静态资源
当前工程的web.xml配置的前端控制器DispatcherServlet的<url-pattern>/</url-pattern>是斜线
tomcat的web.xml配置的默认的DefaultServlet的<url-pattern>/</url-pattern>也是斜线
此时,浏览器发送的请求冲突了,会优先被DispatcherServlet进行处理,但是DispatcherServlet无法处理静态资源
若配置了<mvc:default-servlet-handler></mvc:default-servlet-handler>,此时浏览器发送的所有请求都会被DefaultServlet来处理。
若配置了<mvc:default-servlet-handler></mvc:default-servlet-handler>和<mvc:annotation-driven></mvc:annotation-driven>,
则浏览器发送的请求会先被DispatcherServlet来处理,无法处理再交给DefaultServlet来处理。
-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--开启mvc的注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--
视图控制器:为当前的请求直接设置视图名称实现页面跳转
若设置视图控制器,则只有视图控制器所设置的请求会被处理,其他的请求将全部404,
此时必须再配置一个标签来开启mvc的注解驱动(<mvc:annotation-driven></mvc:annotation-driven>)
-->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
<mvc:view-controller path="/to/add" view-name="employee_add"></mvc:view-controller>
</beans>
employee_add.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" th:href="@{/static/css/index_work.css}">
</head>
<body>
<form th:action="@{/employee}" method="post">
<table>
<tr>
<th colspan="2">add employee</th>
</tr>
<tr>
<td>lastName</td>
<td>
<input type="text" name="lastName">
</td>
</tr>
<tr>
<td>email</td>
<td>
<input type="text" name="email">
</td>
</tr>
<tr>
<td>gender</td>
<td>
<input type="radio" name="gender" value="1">male
<input type="radio" name="gender" value="0">female
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="add">
</td>
</tr>
</table>
</form>
</body>
</html>
控制器类 EmployeeController.java
/**
* 添加员工信息
* @param employee
* @return
*/
@RequestMapping(value = "/employee",method = RequestMethod.POST)
public String addEmployee(Employee employee){
//保存员工信息
employeeDao.save(employee);
//重定向到列表功能 /employee
return "redirect:/employee";
}
运行结果
8.5 具体功能:修改功能
先跳转到修改页面,然后再修改页面用PUT请求提交修改
跳转超链接
<!--拼接符号-->
<!--<a th:href="@{'/employee/'+${employee.id}}">update</a>-->
<!--管道符号-->
<a th:href="@{|/employee/${employee.id}|}">update</a>
employee_update.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>employee_update</title>
<link rel="stylesheet" th:href="@{/static/css/index_work.css}">
</head>
<body>
<form th:action="@{/employee}" method="post">
<input type="hidden" name="_method" value="put">
<input type="hidden" name="id" th:value="${employee.id}">
<table>
<tr>
<th colspan="2">update employee</th>
</tr>
<tr>
<td>lastName</td>
<td>
<input type="text" name="lastName" th:value="${employee.lastName}">
</td>
</tr>
<tr>
<td>email</td>
<td>
<input type="text" name="email" th:value="${employee.email}">
</td>
</tr>
<tr>
<td>gender</td>
<td>
<input type="radio" name="gender" value="1" th:field="${employee.gender}">male
<input type="radio" name="gender" value="0" th:field="${employee.gender}">female
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="update">
</td>
</tr>
</table>
</form>
</body>
</html>
控制类 EmployeeController.java
/**
* 跳转到修改员工信息页面
* @param id
* @param model
* @return
*/
@RequestMapping(value = "/employee/{id}",method = RequestMethod.GET)
public String toUpdate(@PathVariable("id") Integer id,Model model){
//根据id来查询员工信息
Employee employee = employeeDao.get(id);
//将员工信息共享到请求域中
model.addAttribute("employee",employee);
//跳转到employee_update.html
return "employee_update";
}
/**
* 修改员工信息
* @param employee
* @return
*/
@RequestMapping(value = "/employee",method = RequestMethod.PUT)
public String updateEmployee(Employee employee){
//修改员工信息
employeeDao.save(employee);
//重定向到列表功能 /employee
return "redirect:/employee";
}
运行结果
8.6具体功能:删除功能
employee_list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" th:href="@{/static/css/index_work.css}">
</head>
<body>
<div id="app">
<table>
<tr>
<th colspan="5">employee list</th>
</tr>
<tr>
<td>id</td>
<td>lastName</td>
<td>email</td>
<td>gender</td>
<td>options(<a th:href="@{/to/add}">add</a> )</td>
</tr>
<tr th:each="employee : ${employees}">
<td th:text="${employee.id}"></td>
<td th:text="${employee.lastName}"></td>
<td th:text="${employee.email}"></td>
<td th:text="${employee.gender}"></td>
<td>
<a @click="deleteEmployee()" th:href="@{|/employee/${employee.id}|}">delete</a>
<!--拼接符号-->
<!--<a th:href="@{'/employee/'+${employee.id}}">update</a>-->
<!--管道符号-->
<a th:href="@{|/employee/${employee.id}|}">update</a>
</td>
</tr>
</table>
</div>
<form action="" method="post">
<input type="hidden" name="_method" value="delete">
</form>
<!--引入vue.js-->
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript">
var vue = new Vue({
el:"#app",
methods:{
deleteEmployee(){
// 获取form表单
var form = document.getElementsByTagName("form")[0];
// 将超链接的ref属性值赋值给form表单的action属性
//event.target 表示当前触发事件的标签
form.action = event.target.href;
// 表单提交
form.submit();
// 阻止超链接的默认行为(阻止超链接跳转到href的地址)
event.preventDefault();
}
}
})
</script>
</body>
</html>
控制类 EmployeeController.java
/**
* 删除员工信息
* @param id
* @return
*/
@RequestMapping(value = "/employee/{id}",method = RequestMethod.DELETE)
public String deleteEmployee(@PathVariable("id") Integer id){
//删除员工信息
employeeDao.delete(id);
//重定向到列表功能 /employee
return "redirect:/employee";
}
运行结果
9. SpringMVC处理ajax请求
9.1 @RequestBody
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>index.html</title>
</head>
<body>
<div id="app">
<h1>index.html</h1> <br>
<input type="button" value="测试SpringMVC处理ajax" @click="testAjax()"><br>
</div>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript">
/**
* axios中文网:axios-js.com/zh-cn
* axios格式:
* axios({
* url:"", //请求路径
* method:"", //请求方式
* params:{}, //请求参数(1、以name=value&name=value的方式发送请求参数;2、不管使用的请求方式是get或post,请求参数都会被拼接到请求地址中;3、此种方式的请求参数可以通过request.getParameter()获取)
* data:{} //请求参数(1、是以json格式发送请求参数;2、请求参数会被保存到请求报文的请求体传输到服务器;3、此种方式的请求参数不可以通过request.getParameter()获取)
* }).then(response=>{
* console.log(response.data);
* });
*/
var vue = new Vue({
el:"#app",
methods:{
testAjax(){
axios.post(
"/spring_mvc_ajax_war_exploded/test/ajax?id=1001",
{username:"admin",password:"123456"}
).then(response=>{
console.log(response.data);
})
}
}
})
</script>
</body>
</html>
控制类 TestAjaxController.java
/**
* 1、@RequestBody:将请求体中的内容和控制器方法的形参进行绑定;
* @param id
* @param requestBody
* @param response
* @throws IOException
*/
@RequestMapping("/test/ajax")
public void testAjax(Integer id, @RequestBody String requestBody, HttpServletResponse response) throws IOException {
System.out.println("id:"+id); //输出结果:id:1001
System.out.println("requestBody:"+requestBody); //输出结果:requestBody:{"username":"admin","password":"123456"}
response.getWriter().write("hello,axios");
}
9.2 @RequestBody获取json格式的请求
pom.xml
<!--导入jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
SpringMVC.xml
<!--开启mvc的注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>index.html</title>
</head>
<body>
<div id="app">
<h1>index.html</h1> <br>
<input type="button" value="测试@RequestBody处理json格式的请求参数" @click="testRequestBody()"><br>
</div>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript">
var vue = new Vue({
el:"#app",
methods:{
testRequestBody(){
axios.post(
"/spring_mvc_ajax_war_exploded/test/RequestBody/json",
{username:"admin",password: "123456",age:23,gender:"男" }
).then(response=>{
console.log(response.data);
})
}
}
})
</script>
</body>
</html>
控制类 TestAjaxController.java
/**
* 2、使用@RequestBody注解将json格式的请求参数转换为java对象(有实体类的)
* (1)导入jackson的依赖
* (2)在SpringMVC的配置文件中设置<mvc:annotation-driven></mvc:annotation-driven>;
* (3)在处理请求的控制器方法的形参位置,直接设置json格式的请求参数要转换的java类型的形参,使用@RequestBody注解标识;
* @param user
* @param response
* @throws IOException
*/
@RequestMapping("/test/RequestBody/json")
public void testRequestBody(@RequestBody User user, HttpServletResponse response) throws IOException {
System.out.println("user:"+user); //输出结果:user:User{id=null, username='admin', password='123456', age=23, gender='男'}
response.getWriter().write("hello,RequestBody");
}
/**
* 2、使用@RequestBody注解将json格式的请求参数转换为java对象(如果没有实体类用map集合)
* @param map
* @param response
* @throws IOException
*/
@RequestMapping("/test/RequestBody/json")
public void testRequestBody(@RequestBody Map<String,Object> map,HttpServletResponse response) throws IOException {
System.out.println(map); //输出结果:{username=admin, password=123456, age=23, gender=男}
response.getWriter().write("hello,RequestBody");
}
9.3 @ResponseBody
index.html
<a th:href="@{/test/ResponseBody}">测试@ResponseBody注解响应浏览器数据</a><br>
控制类 TestAjaxController.java
/**
* 3、@ResponseBody:将所标识的控制器方法的返回值作为响应报文的响应体响应到浏览器
* @return
*/
@RequestMapping("/test/ResponseBody")
@ResponseBody
public String testResponseBody(){
return "success";
}
9.4 @ResponseBody响应浏览器json数据
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>index.html</title>
</head>
<body>
<div id="app">
<h1>index.html</h1> <br>
<input type="button" value="测试@ResponseBody响应json格式的数据(实体类)" @click="testResponseBodyUser()"><br>
<input type="button" value="测试@ResponseBody响应json格式的数据(map集合)" @click="testResponseBodyMap()"><br>
<input type="button" value="测试@ResponseBody响应json格式的数据(list集合)" @click="testResponseBodyList()"><br>
</div>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"></script>
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<script type="text/javascript">
var vue = new Vue({
el:"#app",
methods:{
testResponseBodyUser(){
axios.post(
"/spring_mvc_ajax_war_exploded/test/ResponseBody/json1"
).then(response=>{
console.log(response.data);
})
},
testResponseBodyMap(){
axios.post(
"/spring_mvc_ajax_war_exploded/test/ResponseBody/json2"
).then(response=>{
console.log(response.data);
})
},
testResponseBodyList(){
axios.post(
"/spring_mvc_ajax_war_exploded/test/ResponseBody/json3"
).then(response=>{
console.log(response.data);
})
}
}
})
</script>
</body>
</html>
控制类 TestAjaxController.java
/**
* 常用的java对象转换为json的结果:
* 实体类 --> json对象
* map --> json对象
* list --> json数组
*/
/**
* 4、使用@ResponseBody注解响应浏览器json格式的数据(实体类)
* (1)导入jackson的依赖
* (2)在SpringMVC的配置文件中设置<mvc:annotation-driven></mvc:annotation-driven>;
* (3)将需要转换为json字符串的java对象直接作为控制器方法的返回值,再使用@ResponseBody注解标识控制器方法,就可以将java对象直接转换为json字符串,并响应到浏览器;
* @return
*/
@RequestMapping("/test/ResponseBody/json1")
@ResponseBody
public User testResponseBodyJson1(){
User user = new User(1001,"admin","123456",20,"男");
return user;
}
/**
* 4、使用@ResponseBody注解响应浏览器json格式的数据(Map集合)
* @return
*/
@RequestMapping("/test/ResponseBody/json2")
@ResponseBody
public Map<String,Object> testResponseBodyJson2(){
User user1 = new User(1001, "admin1", "123456", 20, "男");
User user2 = new User(1002, "admin2", "123456", 21, "男");
User user3 = new User(1003, "admin3", "123456", 22, "男");
Map<String,Object> map = new HashMap<>();
map.put("1001",user1);
map.put("1002",user2);
map.put("1003",user3);
return map;
}
/**
* 4、使用@ResponseBody注解响应浏览器json格式的数据(List集合)
* @return
*/
@RequestMapping("/test/ResponseBody/json3")
@ResponseBody
public List<User> testResponseBodyJson3(){
User user1 = new User(1001, "admin1", "123456", 20, "男");
User user2 = new User(1002, "admin2", "123456", 21, "男");
User user3 = new User(1003, "admin3", "123456", 22, "男");
List<User> userList = Arrays.asList(user1, user2, user3);
return userList;
}
运行结果
9.5 @RestController
//@RestController = @Controller + @ResponseBody ; 相当于为类添加了@Controller注解,为类中的每个方法添加了@ResponseBody注解;
@RestController
public class TestAjaxController {
}
10. 文件下载和上传
10.1 文件下载
index.html
<a th:href="@{/test/down}">下载图片</a>
控制类 FileUpAndDownController.java
/**
* SpringMVC实现下载功能
* ResponseEntity:可以作为控制器方法的返回值,表示响应到浏览器的完整的响应报文
*
* @param session
* @return
* @throws IOException
*/
@RequestMapping("/test/down")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws
IOException {
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/img/1.jpg");
//创建输入流
InputStream is = new FileInputStream(realPath);
//创建字节数组 is.available()获取输入流所对应的文件的字节数
byte[] bytes = new byte[is.available()];
//将流读到字节数组中
is.read(bytes);
//创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers,
statusCode);
//关闭输入流
is.close();
return responseEntity;
}
10.2 文件上传
依赖
<!--https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
SpringMVC配置
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
index.html
<!--
文件上传的要求
1、form表单的请求方式必须为post;
2、form表单必须设置属性enctype="multipart/form-data"
-->
<form th:action="@{/test/up}" method="post" enctype="multipart/form-data">
头像:<input type="file" name="photo" value="选择文件"><br>
<input type="submit" value="上传">
</form>
控制类 FileUpAndDownController.java
/**
* SpringMVC实现文件上传功能
*
* @param photo
* @param session
* @return
* @throws IOException
*/
@RequestMapping("/test/up")
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
//获取上传文件的文件名
String filename = photo.getOriginalFilename();
//获取上传的文件的后缀名
String hzName = filename.substring(filename.lastIndexOf("."));
//获取UUID
String uuid = UUID.randomUUID().toString();
//拼接一个新的文件名
filename = uuid + hzName;
//获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//获取当前工程下photo目录的真实路径
String photoPath = servletContext.getRealPath("photo");
//创建photoPath所对应的File对象
File file = new File(photoPath);
//判断file所对应目录是否存在
if (!file.exists()) {
//如果不存在,直接创建
file.mkdir();
}
String finalPath = photoPath + File.separator + filename;
System.out.println(finalPath); // 输出结果为:D:\SSM\SSM\spring_mvc_ajax\target\spring_mvc_ajax-1.0-SNAPSHOT\photo\touxiang1.jpg
//上传文件
photo.transferTo(new File(finalPath));
return "success";
}
11. 拦截器
11.1 拦截器的三种配置方式
方式一:
<!--拦截器的配置-->
<mvc:interceptors>
<!--配置方式一-->
<bean class="com.atguigu.interceptor.FirstInterceptor"></bean>
<ref bean="firstInterceptor"></ref>
</mvc:interceptors>
方式二:外部声明一个bean,然后引用
<!--拦截器的配置-->
<bean id="firstInterceptor" class="com.atguigu.interceptor.FirstInterceptor"></bean>
<mvc:interceptors>
<!--配置方式二:外部声明一个bean,然后引用-->
<ref bean="firstInterceptor"></ref>
<ref bean="firstInterceptor"></ref>
</mvc:interceptors>
方式三:
<!--拦截器的配置-->
<!--扫描控制层组件-->
<context:component-scan base-package="com.atguigu"/>
<mvc:interceptors>
<!--配置方式三:通过在控制器方法上面加上注解,然后扫描控制层组件-->
<ref bean="firstInterceptor"></ref>
</mvc:interceptors>
方法四:
<!--拦截器的配置-->
<!--扫描控制层组件-->
<context:component-scan base-package="com.atguigu"/>
<mvc:interceptor>
<!--配置需要拦截的请求的请求路径。-->
<mvc:mapping path="/**“”>
<!--配置需要排除拦截器的请求的请求路径-->
<mvc:exclude-mapping path="/abc"/>
<!--配置拦截器-->
<ref bean="firstInterceptor"></ref>
</mvc:interceptor>
11.2 拦截器的三个抽象方法
拦截器的三个抽象方法:
1、preHandle():控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
2、postHandle():控制器方法执行之后执行postHandle()
3、afterCompletion():处理完视图和模型数据,渲染视图完毕之后执行afterCompletion()
package com.atguigu.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class FirstInterceptor implements HandlerInterceptor {
/**
* preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返
* 回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("FirstInterceptor--->preHandle");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
/**
* postHandle:控制器方法执行之后执行postHandle()
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("FirstInterceptor--->postHandle");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterCompletion()
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("FirstInterceptor--->afterCompletion");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
11.3 多个拦截器的执行顺序
1、若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行,而postHandle()和afterCompletion()会按照配置的反序执行
2、若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false
的拦截器之前的拦截器的afterCompletion()会执行
12. 异常处理器
12.1 使用xml配置异常处理
SpringMVC.xml
<!--异常处理-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--
key:设置要处理的异常;
value:设置出现该异常时要跳转的页面所对应的逻辑视图;例如error.html
-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--设置共享在请求域中的异常信息的属性名-->
<property name="exceptionAttribute" value="ex"></property>
</bean>
error.html
<h1>异常信息</h1>
<h2 th:text="${ex}"></h2>
TestController
@RequestMapping("hello/test")
public String testHello(){
System.out.println(1/0);
return "success";
}
运行结果:
12.2 使用注解配置异常处理
ExceptionController.java
package com.atguigu.controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
//将当前类标识为异常处理的组件
@ControllerAdvice
public class ExceptionController {
//设置要处理的异常信息
@ExceptionHandler(ArithmeticException.class)
public String handleException(Throwable ex,Model model){
//ex标识控制器方法所出现的异常
model.addAttribute("ex",ex);
//返回异常页面
return "error";
}
}
error.html
<h1>异常信息</h1>
<h2 th:text="${ex}"></h2>
运行结果:
13. 注解配置SpringMVC
src/main/java/com/atguigu/config/WebInit.java
package com.atguigu.config;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
/**
* 代替web.xml文件
*/
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
//设置一个配置类,来代替Spring的配置文件
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
//设置一个配置类,来代替SpringMVC的配置文件
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
//设置SpringMVC的前端控制器DispatcherServlet的url-pattern
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
//设置当前的过滤器
@Override
protected Filter[] getServletFilters() {
//创建编码过滤器
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
//创建处理请求方式的过滤器
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
}
}
src/main/java/com/atguigu/config/SpringConfig.java
package com.atguigu.config;
import org.springframework.context.annotation.Configuration;
/**
* 用来代替Spring的配置文件
*/
@Configuration //将类标识为配置类
public class SpringConfig {
}
src/main/java/com/atguigu/config/WebConfig.java
package com.atguigu.config;
import com.atguigu.interceptor.FirstInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import java.util.List;
import java.util.Properties;
/**
* 用来代替SpringMVC的配置文件
* SpringMVC配置文件中配置的东西:
* 1、扫描组件
* 2、视图解析器
* 3、默认的Servlet
* 4、mvc的注解驱动
* 5、视图控制器
* 6、文件上传解析器
* 7、拦截器
* 8、异常解析器
*/
//将类标识为配置类
@Configuration
// 1、扫描组件
@ComponentScan("com.atguigu.controller")
// 4、开启mvc的注解驱动
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
// 3、默认的Servlet来处理静态资源
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// 5、视图控制器
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
// 6、文件上传解析器
@Bean //@Bean注解可以将标识的方法的返回值作为bean进行管理,bean的id就是当前方法的方法名
public CommonsMultipartResolver multipartResolver(){
return new CommonsMultipartResolver();
}
// 7、拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
FirstInterceptor firstInterceptor = new FirstInterceptor();
registry.addInterceptor(firstInterceptor).addPathPatterns("/**");
}
// 8、异常解析器
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("java.lang.ArthmeticException","error");
exceptionResolver.setExceptionMappings(properties);
exceptionResolver.setExceptionAttribute("ex");
resolvers.add(exceptionResolver);
}
// 视图解析器
//配置生成模板解析器
@Bean
public ITemplateResolver templateResolver() {
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
// ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(webApplicationContext.getServletContext());
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
//生成模板引擎并为模板引擎注入模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
//生成视图解析器并未解析器注入模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
}
14. SpringMVC执行流程
14.1 SpringMVC常用组件
DispatcherServlet:前端控制器 ,不需要工程师开发,由框架提供
作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
DispatcherServlet:处理器映射器 ,不需要工程师开发,由框架提供
作用:根据请求的url、method等信息查找Handler,即控制器方法
Handler:处理器 ,需要工程师开发
作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理
HandlerAdapter:处理器适配器 ,不需要工程师开发,由框架提供
作用:通过HandlerAdapter对处理器(控制器方法)进行执行
ViewResolver:视图解析器 ,不需要工程师开发,由框架提供
作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、
RedirectView
View:视图
作用:将模型数据通过页面展示给用户
14.2 DispatcherServlet初始化过程
DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet生命周期来进行调度。
14.3 DispatcherServlet调用组件处理请求
14.4 SpringMVC的执行流程
- 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。
- DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
a) 不存在
i. 再判断是否配置了mvc:default-servlet-handler
ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误
iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误
b) 存在则执行下面的流程- 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及
Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。- DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
- 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】
- 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。
在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定
的响应信息
b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等;
c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等;
d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中;- Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
- 此时将开始执行拦截器的postHandle(…)方法【逆向】。
- 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model
和View,来渲染视图。- 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
- 将渲染结果返回给客户端。
统一资源定位符(URL):带http协议、IP地址、端口号和资源具体路径;
统一资源标识符(URI):不带协议、IP地址和端口号,只带资源具体路径;