Spring Boot 是一个用于简化 Spring 应用程序配置和部署的框架。它提供了一种快速开发的方式,通过默认配置、自动化配置等特性,使得开发者能够更快捷地构建和部署基于 Spring 的应用。
Spring Boot Web 是 Spring Boot 的一个子模块,它专注于 Web 应用的构建。它简化了创建 Web 应用的过程,包括 RESTful 服务、Web 层的支持、嵌入式服务器等功能。Spring Boot Web 是 Spring Boot 中处理 Web 相关应用的一个功能模块,通常包含 Spring MVC、嵌入式 Tomcat、Jetty 或 Undertow 服务器等。
Spring Boot 是一个完整的框架,而 Spring Boot Web 是其中一个子模块,专注于 Web 开发。
文章目录
- 一、创建SpringBoot 工程
- 二、 HTTP协议
- 2.1 请求协议
- 2.2 响应协议
- 2.2.1 状态码 描述
- 三、分层解耦
- 3.1 三层架构
- 1. 表示层
- 2. 业务逻辑层
- 3. 数据访问层
- 组件之间的协作
- 完整流程示例:
- 3.2 各层中的职责和关系
- 各层的职责和关系:
- 工作流程:
- 3.3 IOC(控制反转)和DI(依赖注入)
- 四、自写一个案例
- 4.1 数据库结构
- 4.2 服务端代码
- 4.2.0 在测试阶段的问题发现
- 4.2.1 POJO
- 4.2.2 Mapper
- 4.2.3 Controller
- 4.2.4 Service
- 4.2.5 Apipost调试
一、创建SpringBoot 工程
package com.itdt.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
System.out.println("sout Hello World");
return "return Hello World";
}
}
在其中内嵌了tomcat服务器
查看依赖
二、 HTTP协议
HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的应用层协议。它是Web的基础,用于在Web浏览器和Web服务器之间传输数据。
2.1 请求协议
- 请求行(Request Line):包括请求方法(GET、POST 等)、请求的URL和使用的HTTP协议版本。
- 请求头部(Headers):包含了各种标识信息,如Accept、User-Agent、Content-Type 等。
- 请求体(Body):对于 POST 请求,请求体中包含了实际要发送给服务器的数据。对于 GET 请求,通常为空
<!DOCTYPE html>
<html>
<head>
<title>Test Hello Controller</title>
</head>
<body>
<form action="http://localhost:8081/hello" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name">
<button type="submit">Submit</button>
</form>
</body>
</html>
-
GET请求数据格式:
-
GET请求将数据附加在URL之后,以
?
分隔URL和传输数据。 -
参数之间使用
&
连接,由键值对组成,如key1=value1&key2=value2
。 -
GET请求适合传输少量数据,因为数据会暴露在URL中,有长度限制。
(1)从服务器获取数据,且数据量较小且不敏感时,可以使用GET请求。
(2)向服务器请求资源,如获取网页、图片等。
(3)通过URL直接传递参数,方便在浏览器中进行测试和调试。
-
-
POST请求数据格式:
- POST请求的数据在请求体中,不会暴露在URL中。
- 请求头中会包含
Content-Type: application/x-www-form-urlencoded
来指示数据格式。 - 参数同样以键值对的形式传输,但不会显示在URL中。
- POST请求适合传输大量数据或敏感信息,如表单提交等。
- 例如GET:
-
例如POST:(POST请求,将请求的参数放入请求体中)
`Accept`: 指定客户端能够接收的内容类型,如`text/html`、`application/json`等。 `Content-Type`: 指定请求体中的数据类型,如`application/json`、`application/x-www-form-urlencoded`等。 `User-Agent`: 包含了客户端的应用程序、操作系统、版本等信息,用于标识客户端身份。 `Host`: 指定请求的目标主机和端口号。 `Cookie`: 包含了客户端的Cookie信息,用于在客户端和服务器之间保持状态。 `Referer`: 指定了当前请求的来源页面URL。 `Origin`: 指定了发起请求的源。
、、
2.2 响应协议
2.2.1 状态码 描述
状态码 | 英文描述 | 中文描述 |
---|---|---|
1xx | Informational | 信息性状态码。服务器收到请求,需要请求者继续操作 |
100 | Continue | 客户端应继续其请求 |
101 | Switching Protocols | 服务器根据客户端的请求切换协议 |
2xx | Success | 成功状态码。请求成功被服务器接收和理解 |
200 | OK | 请求已成功处理 |
201 | Created | 请求已经成功,并且服务器创建了新资源 |
202 | Accepted | 已接受。请求已接受,但尚未处理 |
203 | Non-Authoritative Information | 非权威信息。服务器已成功处理请求,但返回的信息可能来自另一来源 |
204 | No Content | 服务器成功处理请求,但未返回任何内容 |
205 | Reset Content | 重置内容。服务器成功处理请求,需要重置视图 |
206 | Partial Content | 服务器成功处理了部分GET请求 |
3xx | Redirection | 重定向状态码。需要客户端采取进一步操作以完成请求 |
300 | Multiple Choices | 请求的资源存在多种选择,服务器返回列表供选择 |
301 | Moved Permanently | 请求的资源已被永久移动到新位置 |
302 | Found | 找到。请求的资源临时从不同位置响应 |
303 | See Other | 查看其他。请求的资源存在另一个URI,应使用GET、POST方法获取 |
304 | Not Modified | 资源未被修改,客户端可使用缓存数据 |
305 | Use Proxy | 使用代理。请求必须通过指定的代理才能访问 |
307 | Temporary Redirect | 临时重定向。请求的资源临时从不同位置响应 |
4xx | Client Error | 客户端错误状态码。请求包含语法错误或无法完成请求 |
400 | Bad Request | 错误请求。服务器无法理解请求的语法 |
401 | Unauthorized | 未授权。请求要求用户的身份认证 |
403 | Forbidden | 禁止访问。服务器理解请求,但拒绝执行 |
404 | Not Found | 未找到。服务器无法找到请求的资源 |
405 | Method Not Allowed | 方法不允许。请求中指定的方法不被允许 |
406 | Not Acceptable | 不可接受。服务器无法根据请求的内容特性完成请求 |
407 | Proxy Authentication Required | 需要代理身份验证。客户端必须先使用代理认证 |
408 | Request Timeout | 服务器等待请求时发生超时 |
409 | Conflict | 请求导致服务器上的冲突 |
410 | Gone | 消失。请求的资源不再可用 |
411 | Length Required | 需要长度。缺少Content-Length头信息 |
412 | Precondition Failed | 请求头中指定的前提条件无法满足 |
413 | Payload Too Large | 请求的负载过大,服务器拒绝处理 |
414 | URI Too Long | 请求的URI长度超过服务器能处理的长度 |
415 | Unsupported Media Type | 不支持的媒体类型。请求的格式不受支持 |
416 | Range Not Satisfiable | 范围不符合要求。服务器无法满足请求的Range头信息 |
417 | Expectation Failed | 服务器无法满足请求中Expect请求头信息 |
418 | I’m a teapot | 我是茶壶。服务器拒绝尝试用茶壶煮咖啡 |
421 | Misdirected Request | 误导的请求。服务器无法生成适用于请求的响应 |
422 | Unprocessable Entity | 无法处理的实体。请求格式正确,但无法处理请求 |
423 | Locked | 已锁定。资源被锁定,当前操作无法完成 |
424 | Failed Dependency | 依赖关系失败。请求失败,因为请求的先决条件失败 |
425 | Too Early | 服务器不愿意处理请求,因为可能导致重放 |
426 | Upgrade Required | 需要升级。客户端应切换到TLS/1.0 |
428 | Precondition Required | 需要前提条件。请求失败,因为缺少必需的前提条件 |
429 | Too Many Requests | 请求过多。客户端发送的请求过多 |
431 | Request Header Fields Too Large | 请求头字段太大。服务器无法处理请求,因为请求头字段太大 |
451 | Unavailable For Legal Reasons | 由于法律原因不可用。资源因法律原因不可用 |
5xx | Server Error | 服务器错误状态码。服务器无法完成请求 |
500 | Internal Server Error | 服务器内部错误。服务器遇到意外情况,无法完成请求 |
501 | Not Implemented | 未实现。服务器不支持请求的功能 |
502 | Bad Gateway | 错误网关。服务器作为网关或代理,从上游服务器接收到无效响应 |
503 | Service Unavailable | 服务不可用。服务器当前无法处理请求 |
504 | Gateway Timeout | 网关超时。服务器作为网关或代理,但未及时从上游服务器收到响应 |
505 | HTTP Version Not Supported | HTTP版本不支持。服务器不支持请求中所用的HTTP版本 |
507 | Insufficient Storage | 存储空间不足。服务器无法存储完成请求所必须的内 |
510 | Not Extended | 未扩展。客户端需要对请求进一步扩展 |
511 | Network Authentication Required | 需要网络身份验证。客户端需要进行身份验证才能获得网络访问权限 |
三、分层解耦
- 耦合是指不同模块(或类、组件)之间的依赖关系。它衡量的是模块之间的紧密程度。低耦合意味着模块之间的依赖关系较少,模块之间的变化不会相互影响,这通常是设计中的目标。高耦合则意味着模块之间的依赖关系过多,任何一个模块的变化都会影响到其他模块,增加了维护和修改的难度。
- 内聚是指一个模块内部的各个元素之间的紧密程度。它衡量的是模块内部元素的相关性,或者说是模块内部各个功能的联系度。高内聚意味着模块内部的功能高度相关,模块内部的代码是为了实现一个单一的、明确的功能而组织的。低内聚则意味着模块内部的功能没有明确的目标,模块中的各个元素职责分散,缺乏相关性。
3.1 三层架构
在软件开发中,三层架构(Three-Tier Architecture)是一种常见的设计模式,用于将应用程序的不同职责分离,以提高系统的可维护性、可扩展性和重用性。三层架构通常分为 表示层、业务逻辑层 和 数据访问层。
1. 表示层
- 职责:表示层负责处理用户的输入和输出。它是与用户交互的部分,通常通过 Web 页面(前端)来展示信息,接收用户的请求并返回响应。
- 组件:通常包含
Controller
(或RestController
),它负责接收 HTTP 请求并将其委派给业务逻辑层(Service
)。Controller
会根据用户的请求调用相应的服务,并返回响应结果。
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
}
2. 业务逻辑层
- 职责:业务逻辑层负责处理应用程序的核心业务逻辑,它充当了表示层和数据访问层之间的中介。所有复杂的计算、规则判断和数据转换等任务都在这一层进行。业务逻辑层通常包含接口和其实现类(如
Service
和ServiceImpl
),它们负责调用数据访问层(Mapper
)来进行数据库操作,并处理返回结果。 - 组件:
Service
(接口)和ServiceImpl
(实现类),Service
定义了业务操作的接口,ServiceImpl
提供实际的业务逻辑实现。 - 技术:Spring Service、业务逻辑编写等。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserById(Long id) {
return userMapper.findById(id);
}
}
3. 数据访问层
- 职责:数据访问层负责与数据库进行交互,执行 CRUD 操作(创建、读取、更新、删除)等。它直接与数据库进行通信,封装了所有的 SQL 操作。数据访问层通常由
Mapper
或DAO
(Data Access Object)类组成。Mapper
是 MyBatis 中的接口,而DAO
在其他框架中通常代表数据访问的类。 - 组件:
Mapper
或DAO
,这些类负责数据持久化操作,定义数据库查询语句,并将数据库结果返回给业务逻辑层。 - 技术:MyBatis、JPA(Hibernate)、JDBC、Spring Data JPA 等。
@Mapper
public interface UserMapper {
User findById(Long id);
}
组件之间的协作
- 表示层 (
Controller
) 接收用户请求,调用业务逻辑层 (Service
) 进行处理。 - 业务逻辑层 (
ServiceImpl
) 通过调用数据访问层 (Mapper
) 来获取或更新数据。 - 数据访问层 (
Mapper
) 负责与数据库进行交互,执行 SQL 操作。 - 处理结果返回到 表示层 (
Controller
),最终响应给用户。
层级 | 主要职责 | 主要组件(类) | 主要技术 |
---|---|---|---|
表示层 | 处理用户请求和响应 | Controller、RestController | Spring MVC、JSP、Thymeleaf |
业务逻辑层 | 处理业务逻辑 | Service、ServiceImpl | Spring Service |
数据访问层 | 与数据库交互,执行数据操作 | Mapper、DAO | MyBatis、JPA、JDBC |
完整流程示例:
- 前端发起请求:用户通过 Web 浏览器或其他客户端发起请求(如 GET 或 POST)。
- Controller 处理请求:
Controller
接收请求,调用相应的业务逻辑层 (Service
) 来处理业务。 - Service 进行业务逻辑处理:
ServiceImpl
处理复杂的业务逻辑,并可能需要通过Mapper
与数据库进行交互。 - Mapper 查询数据库:
Mapper
执行 SQL 查询,获取数据并返回。 - 业务层处理数据:
ServiceImpl
对数据库返回的数据进行处理和转换。 - Controller 返回响应:
Controller
将处理结果(通常是数据)返回给前端。
3.2 各层中的职责和关系
+-----------+ +-----------+ +----------------+ +-------------+ +-------------+
| Controller| -----> | Service | -----> | ServiceImpl | -----> | Mapper | -----> | POJO |
+-----------+ +-----------+ +----------------+ +-------------+ +-------------+
↑ ↑ ↑ ↑ ↑
(调用接口) (调用实现) (调用数据库操作) (持久化数据) (封装数据)
各层的职责和关系:
-
Controller: – 负责接收请求,调用
Service
层的接口。- 职责:接收前端请求,调用
Service
层进行处理,并返回处理结果给前端。 - 与其他组件的关系:
Controller
接收到前端请求后,调用Service
接口中的方法进行业务处理,最终返回处理结果。
- 职责:接收前端请求,调用
-
Service (业务层接口): – 定义业务逻辑的接口。
- 职责:定义业务逻辑的抽象接口。
Service
层提供了一系列业务方法,用于业务操作的声明,但不包含具体实现。 - 与其他组件的关系:
Service
层由ServiceImpl
实现,Controller
调用Service
接口提供的业务方法。Service
只定义接口,不涉及具体的业务实现细节。
- 职责:定义业务逻辑的抽象接口。
-
ServiceImpl (服务实现类): – 实现业务逻辑,调用
Mapper
层的数据库操作。- 职责:
ServiceImpl
是Service
接口的具体实现,负责处理具体的业务逻辑。它可以调用Mapper
层进行数据库操作,并封装业务操作。 - 与其他组件的关系:
ServiceImpl
调用Mapper
进行数据库操作,获取或更新数据,并将结果返回给Controller
。它的作用是将数据库访问与业务逻辑分开。
- 职责:
-
Mapper (数据访问层): – 负责数据库的操作,将数据存取到 POJO 中。
- 职责:
Mapper
是与数据库交互的层,通常定义了数据库的查询、插入、更新和删除操作。实现通常MyBatis
- 职责:
-
POJO (数据模型): – 表示数据模型,封装数据。
- 职责:POJO 是普通的 Java 对象,通常用于表示数据库中的一行记录或者某些数据结构。它封装了数据,并为这些数据提供 getter 和 setter 方法。
- 与其他组件的关系:POJO 作为数据模型,通常用于与数据库交互,它由
Mapper
来操作,ServiceImpl
会将 POJO 作为处理结果传递给Controller
。
工作流程:
- 用户请求:用户通过前端发起请求,
Controller
接收请求。 - 调用 Service 接口:
Controller
调用Service
接口定义的方法来执行业务操作。 - 业务实现:
ServiceImpl
作为Service
接口的实现,负责具体的业务逻辑,并调用Mapper
进行数据操作。 - 数据库操作:
Mapper
执行数据库操作,将数据从数据库加载到 POJO 中,或者将 POJO 中的数据保存到数据库。 - 返回结果:
ServiceImpl
将结果(通常是 POJO 对象或集合)返回给Controller
,Controller
将处理结果返回给前端。
3.3 IOC(控制反转)和DI(依赖注入)
-
IOC(控制反转)是一种设计原则,它将控制权从应用程序代码中转移出来,由框架或容器来管理对象之间的依赖关系。在IOC中,对象的创建、组装和管理都由框架来完成,而不是由程序员手动管理。
控制反转:指将对象的控制权交给IOC容器,由IOC容器来创建和管理这些对象,IOC里的这些对象也被称为Bean对象。
将Dao 及 Service层的实现类,交给IOC容器管理;为Controller 及 Service注入运行时所依赖的对象。
声明bean的注解有:@Controller、@Service、@Repository、@Component
-
DI(依赖注入)是IOC的一种实现方式,它指的是通过外部注入的方式来提供一个对象所依赖的其他对象。依赖注入可以通过构造函数、属性或方法来实现,目的是减少类之间的耦合度,使代码更易于维护和测试。
DI:IOC容器要为应用程序提供运行时所依赖的资源。资源,指的是对象。
@Autowired注解,默认是按照类型进行注入的。
如果存在多个相同类型的bean,将会报出如下错误:方案一:@Primary、方案二:@Qualifier、方案三:@Resource
@Resource 与 @Autowired区别 ?
@Autowired是Spring框架提供的注解,而@Resource是JavaEE规范提供的
@Autowired默认是按照类型注入,而@Resource默认是按照名称注入
总的来说,IOC是一种设计原则,而DI是实现这一原则的具体方式之一。通过使用IOC和DI,可以使代码更加灵活、可扩展和易于测试。
四、自写一个案例
4.1 数据库结构
create table users
(
id int auto_increment
primary key,
username varchar(50) not null comment '用户名',
password varchar(255) default '12345678' not null comment '加密后的密码',
email varchar(100) not null comment '用户邮箱',
phone varchar(11) not null,
user_type int not null comment '用户类型(''landlord'', ''tenant'', ''buyer'', ''seller'')',
created_at timestamp default CURRENT_TIMESTAMP not null comment '创建时间',
updated_at timestamp default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
avatar_url varchar(255) null comment '用户头像URL',
gender int null comment '用户性别(男、女)'
)
comment '用户表,存储所有用户信息';
下面是模拟数据
4.2 服务端代码
-
POM 安装分页查询分页插件PageHelper,JAXBJava类映射到XML表示形式
创建项目
我的application.yml
spring:
application:
name: GraduationPro
datasource:
url: 填写你的实际url
driver-class-name: com.mysql.cj.jdbc.Driver
username:填写你的实际username
password: 填写你的实际password
servlet:
multipart:
#最大单个文件大小
max-file-size: 10MB
#最大请求大小(包括所有文件和表单数据)
max-request-size: 100MB
#Mybatis的相关配置
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#开启驼峰命名映射开关
map-underscore-to-camel-case: true
#配置事务管理日志级别
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug
server:
port: 8081
4.2.0 在测试阶段的问题发现
这是由于pom.xml,把下面的这个删去,然后在右侧的maven配置中clean一下重新刷maven,最终重新build一下代码即可
4.2.1 POJO
package com.example.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.sql.Timestamp;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private String email;
private String phone;
private Integer userType;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Timestamp createdAt;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Timestamp updatedAt;
private String avatarUrl;
private Integer gender;
}
统一相应结果
public class Result {
private Integer code; //编码:1成功,0为失败
private String msg; //错误信息
private Object data; //数据
public static Result success() {
Result result = new Result();
result.code = 1;
result.msg = "success";
return result;
}
public static Result success(Object object) {
Result result = new Result();
result.data = object;
result.code = 1;
result.msg = "success";
return result;
}
public static Result error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 0;
return result;
}
}
4.2.2 Mapper
package com.example.mapper;
import com.example.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
@Select("select * from user where id = #{id}")
User getUserById(Integer id);
void save(User user);
}
在resoucrs资源下,新建一个同级的包,切记是以‘/'分割的
在其中同名的xml写(此为复杂情况使用mybatis)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<!-- 批量分隔符-->
<!-- UserMapper.xml -->
<insert id="save" parameterType="com.example.pojo.User">
INSERT INTO user (
username, password, email, phone, user_type, created_at, updated_at, avatar_url, gender
) VALUES (
#{username}, #{password}, #{email}, #{phone}, #{userType}, #{createdAt}, #{updatedAt}, #{avatarUrl}, #{gender}
)
</insert>
</mapper>
4.2.3 Controller
package com.example.controller;
import com.example.pojo.Result;
import com.example.pojo.User;
import com.example.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
// 查询指定用户
@GetMapping("/{id}")
public Result getUserById(@PathVariable Integer id) {
log.info("正在查询id为{}的用户", id);
User user = userService.getUserById(id);
return Result.success(user);
}
@PostMapping
public Result save(@RequestBody User user) {
log.info("用户{}注册", user);
userService.save(user);
return Result.success();
}
}
4.2.4 Service
package com.example.service;
import com.example.pojo.User;
public interface UserService {
void save(User user);
User getUserById(Integer id);
}
其实现类
package com.example.service.impl;
import java.time.LocalDateTime;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void save(User user) {
user.setCreatedAt(LocalDateTime.now());
user.setUpdatedAt(LocalDateTime.now());
userMapper.save(user);
}
@Override
public User getUserById(Integer id) {
return userMapper.getUserById(id);
}
}
4.2.5 Apipost调试
还有json格式的新增用户