SpringMVC
目录
# 一、WEB阶段
1、BaseServlet
- 在做什么?
- 统一接收请求,根据url进行请求分发到具体的Servlet来处理
- 具体的Servlet返回处理结果到BaseServlet,由BaseServlet统一得出响应结果给浏览器
- 客户端请求都通过servlet-mapping中url-pattern来找到对应Servlet来处理具体的请求
- 每个servlet在处理请求时,都会默认调用service方法。而我们自己写的Servlet继承了BaseServlet,而BaseServlet继承了HttpServlet,所以BaseServlet的service方法就作为了所有请求的入口方法,根据请求url中的标识参数【method】通过反射调用相应的方法真正的处理请求。【处理业务逻辑】
- 返回一个具体的响应结果,把这个结果交给BaseServlet的service方法,通过判断响应结果,得出最终响应的方式
二、SpringMVC概述
1、是Spring Frameworker 的一个子模块
2、是一个非常优秀的MVC框架【功能类似于Servlet】
三、SpringMVC快速入门
1、需求
浏览器发送一个请求,由控制器来接收请求,并调用指定方法来处理请求,最后给出响应结果到客户端
2、实现步骤
- 创建一个maven工程,打包方式指定成war包
- 导入依赖
- 在web.xml中配置一个前端控制器【SpringMVC的心脏】
- 编写一个控制器
- 编写SpringMVC的配置文件
- 启动项目,测试
3、具体实现
3.1 创建一个maven工程,打包方式指定成war包
- pom.xml
<packaging>war</packaging>
- 补全目录
- src/main/webapp/WEB-INF/web.xml
3.2 导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
</dependencies>
3.3 在web.xml中配置一个前端控制器【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>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- / : 拦截所有资源,除jsp之外 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
3.4 编写一个控制器【HelloController】
@Controller
public class HelloController {
@RequestMapping("/hello")
public String helloWorld(){
System.out.println("HelloController hello() running");
return "/WEB-INF/success.jsp";
}
}
- 增加一个jsp页面
- /WEB-INF/success.jsp
3.5 编写SpringMVC的配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 组件扫描 -->
<context:component-scan base-package="com.qf.java2007.controller"></context:component-scan>
</beans>
3.6 启动项目,测试
- 配置tomcat
- 部署springmvc应用
- 浏览器输入访问url,进行测试
测试效果 |
---|
![]() |
4、Spring执行流程简化版
Spring执行流程简化版 |
---|
![]() |
| |
5、细节问题
<!-- 开启注解驱动 -->
<!-- 正常情况下,这个标签是必配的【直接写上】
会帮我们配置处理器映射器和处理器适配器
-->
<mvc:annotation-driven />
四、SpringMVC执行流程【详细版】
SpringMVC执行流程 |
---|
![]() |
五、SpringMVC应用
1、Handler是如何接收请求参数
1.1 基本类型
八大基本数据类型及包装类,String
1.1.1 解析
handler中的形参名字 跟 请求参数名一致,就可以直接封装参数
如:请求url:http://localhost:8080/test01?name=jack name:参数名 jack:值
handler xxx(String name)
1.1.2 代码
- 页面
<a href="test01?name=jack&num=10&money=100.6">get请求传参 test01</a>
- Handler
/**
* 接收基本类型的请求参数
* @param name
* @param num
* @param money
* @return
*/
@RequestMapping("/test01")
public String test01(String name, Integer num, Double money){
System.out.println("name = " + name);
System.out.println("num = " + num);
System.out.println("money = " + money);
return SUCCESS;
}
1.2 实体类型
1.2.1 解析
handler中的形参是一个实体类型,它的属性名 跟前台传递参数的名称一一对应【调的是setter方法】
1.2.2 代码
- 实体类【User.java】
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
//规定createTime属性的格式为 yyyy-MM-dd HH:mm:ss
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
//本质上是通过解析该注解,把string转成date类型,由类型转换器完成的。
private Date createTime;
}
- 页面
<form action="test02" method="post">
<input type="hidden" name="id" value="10"/><br/>
用户名:<input type="text" name="username" /><br/>
密码:<input type="text" name="password" /><br/>
创建时间:<input type="text" name="createTime" /><br/>
<input type="submit" value="传递参数为实体对象"/>
</form>
- Handler
@RequestMapping("/test02")
public String test02(User user){
//springmvc 默认支持的时间格式是 yyyy/MM/dd HH:mm:ss
//如果需要定制时间格式。 @DateTimeFormat
System.out.println("user = " + user);
return SUCCESS;
}
1.2.3 可能出现的错误
- 400:参数封装错误
1.3 @RequestParam注解
- 页面
<a href="test03?size=5">test03 首页</a>
<a href="test03?pageNo=2&size=5">test03 分页</a>
- handler
/**
* @RequestParam : 代表请求参数
* name : 指定请求参数的名称
* required : 指定请求参数是否必须
* defaultValue : 默认值【如果前台没有传递该参数,则使用defaultValue】
* @param pageNo
* @param pageSize
* @return
*/
@RequestMapping("/test03")
public String test03(Integer pageNo,
@RequestParam(name = "size", required = false, defaultValue = "0") int pageSize){
System.out.println("pageNo = " + pageNo);
System.out.println("pageSize = " + pageSize);
return SUCCESS;
}
1.4 请求参数为数组
- 页面
<form action="test04" method="post">
ids: <input type="checkbox" name="ids" value="1"/>1<br/>
<input type="checkbox" name="ids" value="2"/>2<br/>
<input type="checkbox" name="ids" value="3"/>3<br/>
<input type="submit" value="传递参数为数组"/>
</form>
- handler
/**
* 请求参数为数组
* @param ids
* @return
*/
@RequestMapping("/test04")
public String test04(Integer[] ids){
if(ids != null && ids.length > 0) {
System.out.println("ids = " + Arrays.asList(ids));
}
return SUCCESS;
}
1.5 集合参数【了解】
当前集合类型参数必须封装到一个JavaBean中,作为属性存在
- 页面
<form action="test05" method="post">
<input type="hidden" name="id" value="10"/><br/>
用户名:<input type="text" name="username" /><br/>
密码:<input type="text" name="password" /><br/>
创建时间:<input type="text" name="createTime" /><br/>
List(爱好):<input name="hobbies[0]" value="Java"/>
<input name="hobbies[1]" value="Game"/><br/>
maps:<input name="maps['aa']" value="AA"/>
<input name="maps['bb']" value="BB"/>
<input type="submit" value="传递参数为集合"/>
</form>
- handler
/**
* 请求参数为集合
* 集合参数必须作为JavaBean的属性存在
* @param user
* @return
*/
@RequestMapping("/test05")
public String test05(User user){
System.out.println("user = " + user);
return SUCCESS;
}
1.6 post请求解决中文乱码
- web.xml配置中文乱码过滤器
<!-- 解决post请求乱码 -->
<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>
1.7 @PathVariable【路径传参】
对后期学习SpringMVC的Restful风格有关系
参数在url中
- 页面
<a href="test06/jack/10/100.6">路径传参</a>
- handler
/**
* url : /test06/jack/10/100.6
* 如果是小数,数据会丢失小点后后面的
* @PathVariable 用来绑定URL路径上的参数
* name : 指定url路径上{}中名字,以此来获取对应的参数
* required : 是否必须,默认值就是true。
* 如果指定成false,会报 No mapping for GET /test07
* 后期跟 restful 有点关系
* @param name
* @param age
* @param balance
* @return
*/
@RequestMapping("/test06/{name}/{age}/{b}")
public String test06(@PathVariable String name,
@PathVariable Integer age,
@PathVariable(name = "b", required = false) Double balance){
System.out.println("name = " + name);
System.out.println("age = " + age);
System.out.println("balance = " + balance);
return SUCCESS;
}
2、响应结果类型
2.1 响应结果是字符串
2.1.1 普通字符串
是一个逻辑视图名,经过视图解析器解析成物理视图
2.1.2 带前缀的字符串
- forward: 转发【不经过视图解析器】
- redirect: 重定向【不经过视图解析器】
- handler
@RequestMapping("/test11")
public String test11(Model model){
System.out.println("ResponseController test11");
//使用model传递数据,底层介质request
model.addAttribute("address","杭州");
model.addAttribute("school","千锋教育");
//1.可以转发到具体的视图
return "forward:/WEB-INF/success.jsp";
//2.可以转发到具体的handler
//return "forward:/hello";
}
@RequestMapping("/test12")
public String test12(){
System.out.println("ResponseController test12");
//1.可以重定向到具体的视图
//return "redirect:/index.jsp";
//2.可以重定向到具体的handler
return "redirect:/hello";
}
2.2 响应结果是ModelAndView
ModelAndView:模型和视图
Model:存放数据的模型
View:响应的视图
- handler
@RequestMapping("/test13")
public ModelAndView test13(){
System.out.println("ResponseController test13");
ModelAndView mav = new ModelAndView();
//封装参数,底层存储的介质是 request
mav.addObject("name", "刘胖子");
mav.addObject("gender", "男");
//设置视图,走视图解析器
mav.setViewName("test");
return mav;
}
2.3 响应结果被@ResponseBody修饰
@RequestMapping("/test14")
//@ResponseBody //返回的结果,直接当做响应体给到浏览器
public @ResponseBody String test14(){
System.out.println("ResponseController test14");
return "GOOD";
}
后期经过响应一些json串
六、获取原生ServletAPI
1、直接在hander的形参中定义即可
- handler
/**
* 获取原生ServletAPI
* @param request
* @param response
* @param session
* @return
*/
@RequestMapping("/test11")
public String test11(HttpServletRequest request,
HttpServletResponse response,
HttpSession session){
System.out.println("request = " + request);
System.out.println("response = " + response);
System.out.println("session = " + session);
String realPath = session.getServletContext().getRealPath("/");
System.out.println("realPath = " + realPath);
return "success";
}
/**
* 如果使用原生的ServletAPI,就相当于操作一个Servlet
* @param request
* @param response
* @param session
* @throws ServletException
* @throws IOException
*/
@RequestMapping("/test12")
public void test12(HttpServletRequest request,
HttpServletResponse response,
HttpSession session) throws ServletException, IOException {
System.out.println("request = " + request);
System.out.println("response = " + response);
System.out.println("session = " + session);
String realPath = session.getServletContext().getRealPath("/");
System.out.println("realPath = " + realPath);
//request.getRequestDispatcher("/WEB-INF/success.jsp").forward(request, response);
response.sendRedirect(request.getContextPath() +"/redirect.jsp");
}
2、直接从Spring容器中注入即可
- HttpServletRequest可以使用@Autowired从SpringMVC容器中获取,当做属性注入到Controller中,是一个线程安全的请求对象,功能跟原生Request对象相近。最好仅用于域对象操作
- 目的就是为了让多个方法同时使用一个Request对象即可。
- handler
// HttpServletRequest 作为成员变量在controller中存在,是没有线程安全问题的。
// ThreadLocal
@Autowired
HttpServletRequest request;
@RequestMapping("/test13")
public String test13() throws Exception {
System.out.println("request = " + request.getClass().getName());
request.setAttribute("username", "李四");
return "success";
}
七、释放静态资源
1、DispatcherServlet的url-pattern以后缀的方式去拦截资源
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- *.do 或者 *.action -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
2、让经过DispatcherServlet的资源,如果没有找到对应的Handler,交由tomcat默认的Servlet来处理
- springmvc配置文件
<!-- 开启tomcat默认servlet的使用 -->
<!-- 方式二: 可以释放静态资源 -->
<mvc:default-servlet-handler/>
3、手动释放
- springmvc配置文件
<!-- 第三种: 手动释放静态资源 -->
<!--
mapping : 映射路径
location : 静态资源存放的目录
-->
<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>
<mvc:resources mapping="/html/**" location="/html/"></mvc:resources>
<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
八、处理json
1、@ResponseBody
直接把结果响应给页面
- 如果字符串,就原封不动响应
- 如果对象或者集合,转成json给出响应【不要忘了加jackson依赖】
@RequestMapping("/test17")
@ResponseBody
public User test17() {
User user = new User();
user.setUname("李四");
user.setPassword("123456");
user.setBirthday(new Date());
user.setGender(1);
return user;
}
@RequestMapping("/test18")
@ResponseBody
public List<User> test18() {
User user1 = new User("刘备", "111", new Date(), 1);
User user2 = new User("关羽", "222", new Date(), 2);
User user3 = new User("张飞", "333", new Date(), 0);
List<User> list = new ArrayList<>();
list.add(user1);
list.add(user2);
list.add(user3);
return list;
}
2、@RequestBody
把请求参数为json的字符串数据转成对应的实体或集合
- 页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test.html</title>
<script src="../js/jquery-3.4.1.min.js"></script>
</head>
<body>
<button onclick="testAjax();">传递一Json到后台</button>
<!--<script>
var user = {
uname : "赵六",
password : "123456",
birthday : new Date(),
gender : 1
};
function testAjax() {
$.ajax({
url : "../test19",
type : "POST",
data : JSON.stringify(user),
contentType : "application/json",
//dataType : ,
success : function (result) {
alert(result);
},
error: function () {
alert("出错了");
}
});
}
</script>-->
<script>
var users = [
{uname : "赵六",password : "123456",birthday : new Date(),gender : 1},
{uname : "钱七",password : "456456",birthday : new Date(),gender : 0}
];
function testAjax() {
$.ajax({
url : "../test20",
type : "POST",
data : JSON.stringify(users),
contentType : "application/json",
//dataType : ,
success : function (result) {
alert(result);
},
error: function () {
alert("出错了");
}
});
}
</script>
</body>
</html>
- handler
/**
* @RequestBody : 就是把请求的json格式参数转成实体或集合
* @param user
* @return
*/
@RequestMapping("/test19")
@ResponseBody
public String test19(@RequestBody User user) {
System.out.println(user);
System.out.println("-----------------------");
return "success";
}
@RequestMapping("/test20")
@ResponseBody
public String test20(@RequestBody List<User> list) {
System.out.println(list);
System.out.println("-----------------------");
return "success";
}
3、@RestController
@Controller + @ResponseBody
标记在类上
package com.qf.java2007.controller;
import com.qf.java2007.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author ghy
* @version 1.0
*/
//@Controller
//@ResponseBody //标记在类上,相当于当前Controller中的Handler方法返回都标记了@ResponseBody一个注解
@RestController //等同于@Controller + @ResponseBody
public class Demo3Controller {
@RequestMapping("/test21")
public String test21() {
System.out.println("Demo3Controller test21");
return "success";
}
@RequestMapping("/test22")
public User test22() {
User user = new User();
user.setUname("李四");
user.setPassword("123456");
user.setBirthday(new Date());
user.setGender(1);
return user;
}
}
4、如何更换Json转换工具
使用fastjson
- Springmvc配置
<!-- 无敌模式 -->
<mvc:annotation-driven>
<!-- 安装FastJson,转换器 -->
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<!-- 声明转换类型:json -->
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
九、@RequestMapping
建立请求URL和处理请求Handler之间的映射关系
根据请求URL找到对应处理请求的Handler
- handler
/**
* @RequestMapping
* 有一个属性必不可少,就是path或者value
* path : 指定请求的url们。【多个请求URL可以指定同一个handler来处理,url(n) --> handler(1),反之不行】
* value : 作用跟path一样
* method : 限定请求方式,可以不写【任何请求方式都可以】。
* method = RequestMethod.POST
* params : 指定请求必须指定请求参数
* headers : 指定请求必须包含指定请求头
* @return
*/
@RequestMapping(path = {"/", "/index", "/hello/index"},
params = {"username=jack"},
headers = {"User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36"})
@ResponseBody
public String test33(){
return "success";
}