1.SpringMVC
1.1 概述
- Spring MVC 是 Spring 提供的一个基于 MVC 设计模式 的轻量级 Web 开发框架,本质上相当于 Servlet相当于对其的进一步封装 核心组件
DispatcherServlet
。- Spring MVC框架 内部采用松耦合、可插拔的组件结构, 具有高度可配置性,比起其他的 MVC 框架更具有扩展性和灵活性。
- 我们的 实体类(POJO) 就是Model层,我们的JSP就是 视图层(View),我们的Controller就是控制层。
- Model: 负责对请求进行处理,并将结果返回给 Controller;
- View: 负责将请求的处理结果进行渲染,展示在客户端浏览器上;
- Controller: 是 Model 和 View 交互的纽带;主要负责接收用户请求,并调用 Model 对请求处理,然后将 Model 的处理结果传递给 View。
1.2 MVC模型是什么 ?
- MVC 模式,全称为
Model-View-Controller
(模型-视图-控制器)模式,它是一种软件架构模式。- 目标: 将软件的用户界面(即前台页面)和 业务逻辑分离,使代码具有更高的可扩展性、可复用性、可维护性以及灵活性。
- 特点:可以达到一个松耦合的效果,形成了可插拔的模块化,分层和解耦。
1.2.1 模型图
- web项目的演变。
-
早期的JavaWeb的项目应用。
-
MVC 模式将应用程序划分成模型(Model)、视图(View)、控制器(Controller)等三层。
-
- 现在主流基于SSM三大框架开发都是在MVC上继续演化,出现了 ,三层架构:UI表示层,数据访问层DAL,业务逻辑层BLL,
- 表示层::HTML、JSP 等前台页面以及后台的 Servlet,即它相当于 MVC 模式中的 View 层 + Controller 层。
- 数据层,则只包含了 Dao 接口及其实现类(DaoImpl)的代码,即它相当于 MVC 模式中 Model 层的一部分,并不包含 Service 和实体类
- 业务层,则只包含了 Service 接口及其实现类(Servicelmpl)的代码,即它相当于 MVC 模式中 Model 层的一部分,并不包含 Dao 和实体类。
1.2.2 MVC的工作流程
- 工作流程图
- 工作流程详细描述:
- 用户发送请求到服务器(前端控制器DispatcherServlet,充当调度者,去调度每一个角色),DispatcherServlet收到请求调用HandlerMapping处理器映射器(根据浏览器URL地址值,找到能处理请求的类名和方法名称)。
- DispatcherServlet调用HandlerAdapter处理器适配器,开始正式处理业务,HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
- Controller执行完成返回ModelAndView。
- HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器,ViewReslover解析后返回具体View。
- View 视图渲染数据后,将数据填充到视图中,最终展示给用户。
1.3 课堂案例
1.3.1 将手机信息展示到页面
-
需求:
- 访问网页信息,http://localhost:8080/phone/info
- 页面显示相应的手机信息。
-
创建 Maven project。
- 注意:选择 SpringBoot 的版本星号,JDK8支持3.0以下的,以上的需要 17支持。
-
准备创建类。
- 创建
DemoApplication.class
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
- 创建
Phone.class
public class Phone { private Integer id; private String brand; public Phone(Integer id, String brand, String colour, String county) { this.id = id; this.brand = brand; } public Phone() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } @Override public String toString() { return "Phone{" + "id=" + id + ", brand='" + brand + '\'' + '}'; } }
- 创建
PhoneController.class
@RestController @RequestMapping("/phone") public class PhoneController { @RequestMapping("/info") public String info(){ Phone phone = new Phone(13, "Max", "紫色", "USA"); String s = phone.toString(); return s; } @RequestMapping("/info2") public Phone info2() { Phone phone = new Phone(); phone.setId(18); phone.setBrand("phone"); return phone; } //TODO 测试: 可以返回基本数据类型到页面么? 如数组? @RequestMapping("/arr") public int[] arr() { int[] a = {1,2,3,4}; return a; } }
- 创建
-
启动SpringBoot,访问地址,
http://localhost:8080/phone/info
,http://localhost:8080/phone/info2
- 得到返回结果
Phone{id=13, brand='Max'}
,{"id":18,"brand":"phone"}
。
- 得到返回结果
2. SpringMVC解析处理请求参数
2.1 GET ,POST,RESTFul
- 当客户端打开浏览器要访问服务器时,可能会带着一些 http协议 请求参数过来。这时,服务器需要获取http参数进行业务处理。
- 如何处理http请求并获取参数呢,常见的两种方式:
- GET方式,向特定的资源发出请求,并返回实体,有固定的写法,而且数据有最大长度,超出就不行。
- 例如:
http://localhost:8080/phone/add?id=1&name=周杰伦&age=38
。
- 例如:
- POST方式,向指定资源提交数据进行处理请求(例如提交表单或者上传文件),数据被包含在请求体中。
- GET方式,向特定的资源发出请求,并返回实体,有固定的写法,而且数据有最大长度,超出就不行。
- RESTFul 方式 为了简化GET请求的写法,可以使用RESTFul方式,用法如下:
- 需要使用注解
@PathVariable
来获取请求路径中的参数值,@PathVariable
用来绑定值 - 通过{???}获取路径中传递来的值
- 以前GET的访问方式即将被简化成,
http://localhost:8080/phone/add/1/周杰伦/44
- 需要使用注解
2.2 处理Get请求参数
2.2.1 编写后端
-
页面的名称和后台形参的名称要保持一致。
User.class
类
public class User { private int id; private String name; private int age; private double price; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", price=" + price + '}'; } }
CetController.class
类
@RestController @RequestMapping("/phone") public class GetController { //1. 解析get获取的Url地址值,如: key=value 中的value @Test //使用单元测试 public void get(){ //http://localhost:8080/phone/insert?id=1&name=周杰伦&age=38 String s = "http://localhost:8080/phone/add?id=1&name=周杰伦&age=38"; String[] split = s.split("\\?")[1].split("&"); for (String data:split) { //[id=1, name=周杰伦, age=38] ,然后按照等号切获取data[1],就是value值 String value = data.split("=")[1]; System.out.println(value); // 1, 周杰伦,38. } } /*2.如果使用框架处理问题。 http://localhost:8080/phone/get2?id=1 */ @RequestMapping("get2") public int get2(int a){ //页面访问值id 需要和 形式参数保持一致。 return a; } //2.1 http://localhost:8080/phone/get3?name=周杰伦&age=44 @RequestMapping("get3") public String get3(String name,int age){ return "请求的参数name= :"+name+ " 年龄age= "+ age; } /** * 3. 假设有参数很多了该怎么办? * 框架能给我们做什么 ? * 这样如何解析: http://localhost:8080/phone/get4?id=1&name=周杰伦&age=44&price=2.2 */ // public String get4(int id,String name,int age,double price){} @RequestMapping("get4") public User get4(User u){ return u; } }
2.2.2 编写前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 超连接一个路径,点击时会跳转到方法-->
<a href="http://localhost:8080/phone/get4?id=1&name=周杰伦&age=44&price=2.2">点我,得到你想要的</a>
</body>
</html>
2.3 处理RESTFul 请求参数
-
主要简化了get请求的提交,如果是restful 请求,需要使用
@RequestParam(“页面名称”),
获取路径中传递的值。- 创建
RunApp.class
该类为资源包里,或者其他类的上一级或者同级 。 不同包则无效。
@SpringBootApplication public class RunApp { public static void main(String[] args) { SpringApplication.run(RunApp.class, args); } }
- 创建
2.3.1 前后端代码处理
-
创建
Controller.class
,形式参数上与 通过{ }获取路径中传递来的值。- @PathVariable在参数注解,获取传递路径上参数,需要与形参保持一致。
@RestController @RequestMapping("user") public class Controller { //restful 请求需要 在RequestMapping 中加入{}参数 //并且需要在 形式参数上@PathVariable 绑定值 @RequestMapping("info/{name}/{age}") public String info(@PathVariable String name, @PathVariable Integer age){ return "返回的结果 name= "+name+" 年龄 age= "+age; } //2.<a href="http://localhost:8080/user/info2/周杰伦/44/中国">restful解析</a> // 封装对象,自动赋值访问。 @RequestMapping("info2/{name}/{age}/{conutry}") public Person info2(Person person){ return person; } }
-
前端页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 超连接一个路径,点击时会跳转到方法--> <a href="http://localhost:8080/user/info/周杰伦/20">restful解析</a> <a href="http://localhost:8080/user/info2/周杰伦/44/中国">restful解析2</a> </body> </html>
2.4 处理Post请求的参数
2.4.1 前期准备
- 项目结构
- 创建Maven project 结构如下:
- 创建Maven project 结构如下:
- 需求处理
- 单个参数 name
- 读个参数 name age
- 对象 Student
- RestFul 形式
- 准备表单
Students.html
,因为post需要以表单的形式进行提交。- 创建 Students.html 在项目结构中。
- 创建 Students.html 在项目结构中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>student</title>
</head>
<form action="http://localhost:8080/stu/add" method="post">
<table>
<h2>学生表单Post测试</h2>
<tr>
<td>
姓名:<input type="text" name="name" placeholder="请输入姓名..."/>
</td>
</tr>
<tr>
<td>
年龄:<input type="text" name="age" placeholder="请输入年龄..." />
</td>
</tr>
<tr>
<td>
性别:(单选框)
<input type="radio" name="sex" checked="checked" value="0"/>男
<input type="radio" name="sex" value="1" />女
</td>
</tr>
<tr>
<td>
爱好:(多选)
<input type="checkbox" name="hobby" checked="checked" value="lq"/>篮球
<input type="checkbox" name="hobby" value="rap"/>rap
<input type="checkbox" name="hobby" value="cg"/>唱歌
</td>
</tr>
<tr>
<td>
选择方向:
<select name="edu">
<option value ="1">初级课程</option>
<option value ="2">高级课程</option>
</select>
</td>
</tr>
<tr>
<td>
入学时间: <input type="date" name="intime"/>
</td>
</tr>
<tr>
<td>
<input type="submit" value="保存" />
<input type="reset" value="取消" />
</td>
</tr>
</table>
</form>
</body>
</html>
2.4.2 准备后端代码处理
-
创建
Student.class
。- 注意日期类,类型不匹配会报错。
页面报400 IllegalArgumentException: String->Date
。 - 所以需要使用注解进行解析
@DateTimeFormat(pattern = "yyyy-MM-dd")
public class Student { private String name; private Integer age; private String sex; private String[] hobby; private String edu; //时间类型需要处理,网页上是String类型,实际属性是Date类型。 @DateTimeFormat(pattern = "yyyy-MM-dd") private Date intime; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } public String getEdu() { return edu; } public void setEdu(String edu) { this.edu = edu; } public Date getIntime() { return intime; } public void setIntime(Date intime) { this.intime = intime; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + ", hobby=" + Arrays.toString(hobby) + ", edu='" + edu + '\'' + ", intime=" + intime + '}'; } }
- 注意日期类,类型不匹配会报错。
-
创建
StudentController.class
@RestController @RequestMapping("/stu") public class StudentController { @RequestMapping("add") public String add(Student student){ //TODO 可以使用 jdbc 数据库中.... 略 return student.toString();//将结果输出到屏幕。 } }
-
创建启动
RunApp.class
。@SpringBootApplication public class RunApp { public static void main(String[] args) { SpringApplication.run(RunApp.class, args); } }
3.完成前后端整合
3.1 使用Jdbc连接数据库
-
还记得jdbc如何使用么 ?
- 添加数据库的依赖在pom.xml中
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency>
-
创建jdbc的连接步骤
- 单独创建一个方法做业务逻辑操作,如插入。
@RequestMapping("insert")
public void insert(Student s){
//TODO 进行前后端整合,使用jdbc连接数据库
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.创建数据库连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_student?characterEncoding=utf-8",
"root", "root");
//3. 获取传输器,并发送sql语句等待返回结果
String sql = "insert into student values (null,?,?,?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
//4。设置参数每个属性获取值
// ps.setString(1,s.getName());
// ps.setInt(2,s.getAge());
// ps.setString(3,s.getSex());
//4.1 Object 使用多态设置减少
ps.setObject(1,s.getName());
ps.setObject(2,s.getAge());
ps.setObject(3,s.getSex());
//ps.setObject(4,s.getHobby());
ps.setObject(4, Arrays.toString(s.getHobby())); //因为是数组,需要转为String。注意
ps.setObject(5,s.getEdu());
ps.setObject(6,s.getIntime());
ps.executeUpdate();//执行sql执行。
System.out.println("数据插入成功");
} catch (Exception e) {
e.printStackTrace();
}
}
3.2 创建数据库
- 按照 db_student 创建数据库,根据前端逻辑页面创建。
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`age` int DEFAULT NULL,
`sex` varchar(10) DEFAULT NULL,
`hobby` varchar(100) DEFAULT NULL,
`edu` varchar(100) DEFAULT NULL,
`intime` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3
- 注意html上得post提交action
<form action="http://localhost:8080/stu/insert" method="post">
;