自学SpringBoot笔记
概念
什么是SpringBoot?
Spring Boot 是基于 Spring Framework 的一款开源框架,主要用于简化 Spring 应用程序的开发。它通过提供一系列的 开箱即用的功能 和 自动配置,让开发者可以快速构建生产级别的独立应用程序,而无需手动配置大量的 XML 或注解。
Spring Boot 的目标是让开发者能够专注于业务逻辑,减少繁杂的配置工作,从而提升开发效率。
什么是 Spring MVC?
Spring MVC(Model-View-Controller)是 Spring Framework 中的一个模块,专门用于构建基于 HTTP 的 Web 应用程序,它遵循经典的 MVC 架构模式。Spring MVC 提供了一套灵活、强大的工具,用于开发动态 Web 应用或 RESTful API。
通过 Spring MVC,开发者可以轻松处理 HTTP 请求、业务逻辑和视图层的渲染,同时保持应用的模块化和可维护性。
Spring MVC 的核心概念
Spring MVC 基于 Model-View-Controller 模式,将应用分为以下三个部分:
1. Model(模型)
- 表示应用程序的数据或业务逻辑。
- 通常由 POJO(Plain Old Java Object)或 DTO(数据传输对象)组成。
- 在实际开发中,
Model
通常是从数据库中获取的业务实体。
2. View(视图)
- 负责将数据(Model)渲染为用户可以看到的页面或响应数据。
- 支持多种视图技术,例如 JSP、Thymeleaf、FreeMarker 等。
- 对于 RESTful 应用,视图层通常是 JSON 或 XML 格式的响应。
3. Controller(控制器)
- 负责接收用户请求,并将其分发给合适的服务或处理逻辑。
- 控制器将数据与视图进行绑定,最终返回一个完整的响应。
Spring MVC 的工作流程
- 用户通过浏览器发送一个 HTTP 请求。
- 请求被 Spring MVC 的 DispatcherServlet 拦截。
- DispatcherServlet 将请求分发给合适的 Controller。
- Controller 调用业务逻辑层或服务层,获取数据并封装为 Model。
- DispatcherServlet 将返回的数据传递给指定的视图(View)。
- 视图渲染最终的 HTML 页面或响应数据,并返回给用户。
Spring MVC 的核心组件
以下是 Spring MVC 的几个核心组件及其作用:
1. DispatcherServlet
- 是 Spring MVC 的核心,负责拦截所有 HTTP 请求。
- 它将请求分发给适当的处理器(Controller)并协调其他组件(如视图解析器和模型绑定器)。
2. Controller
- 使用注解(如
@Controller
或@RestController
)标注的类,用于处理业务逻辑。 - 处理用户请求并返回数据或视图。
3. ModelAndView
- 一个对象,用于封装数据(Model)和视图(View)。
- Controller 可以返回
ModelAndView
来指定渲染的数据和视图名称。
4. ViewResolver(视图解析器)
- 负责将逻辑视图名称转换为具体的视图。
- 例如,将返回的视图名
home
映射到home.jsp
或home.html
。
5. HandlerMapping
- 负责将用户请求 URL 映射到具体的 Controller 方法。
6. Model
- 用于在 Controller 和 View 之间传递数据。
springboot架构
首先我们先看下面这张图,待会会根据该图中的各个层进行项目创建:
Maven上手
这里我们使用IDEA进行Maven项目的创建,这里我们选择Maven创建:
这里我们选择springWeb
和mysql
:
pom.xml
如果我们后续还需要添加依赖的话,可以直接在项目的pom.xml中的<dependencys>
中添加依赖:
配置文件
在application.properties
中,我们可以配置数据库的连接、后端端口等内容:
项目结构
仿照上图的SpringBoot架构,我们创建以下文件夹来划分各个层次:
什么是 POJO?
POJO 是 Plain Old Java Object 的缩写,意为“普通的旧式 Java 对象”。它是一个 纯粹的 Java 对象,不依赖于任何特定的框架或库,通常只包含 属性、构造方法、Getter 和 Setter 方法 以及少量的业务逻辑。
POJO 的核心思想是保持对象简单、轻量化,避免引入框架特定的依赖或复杂的特性(如继承特定的类或实现框架接口)。
POJO 的特点
-
轻量化:
- 没有继承任何框架特定的类(如
HttpServlet
)。 - 不实现任何框架特定的接口(如
Serializable
除外)。
- 没有继承任何框架特定的类(如
-
纯粹性:
- 只包含简单的属性(字段)和方法(如 Getter 和 Setter)。
- 不依赖框架代码,适用于任何 Java 环境。
-
无强制要求:
- POJO 不需要遵循特定规范,只要是普通的 Java 对象即可。
POJO 的常见用途
-
数据传输对象(DTO):
- 用于在不同层(如 Controller 和 Service 层)之间传输数据的对象。
- 通常只包含属性和 Getter/Setter 方法。
-
实体类(Entity):
- 用于映射数据库表的类,通常用于 ORM 框架(如 Hibernate、JPA)。
- 每个字段对应数据库表中的一列。
-
模型类:
- 用于封装业务逻辑中的数据模型。
-
配置类:
- 用于封装应用程序的配置信息。
pojo的示例——user
package com.jiaowu.backend.pojo;
import jakarta.persistence.*;
@Table(name = "tb_user")
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="user_id")
private Integer id;
@Column(name="user_name")
private String name;
@Column(name="user_password")
private String password;
@Column(name="user_email")
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
注解总结
以下是代码中各注解的作用和含义:
1. @Table(name = "tb_user")
- 作用:指定该实体类对应的数据库表名称。
- 解释:将类
User
映射到名为tb_user
的数据库表。如果不加该注解,默认表名会与类名一致。
2. @Entity
- 作用:将该类标记为一个 JPA 实体类。
- 解释:
User
类会被 JPA 识别为数据库中的一个表实体,JPA 会根据该类的属性与注解生成对应的数据库表操作。
3. @Id
- 作用:指定该字段为主键。
- 解释:
id
字段是数据库表tb_user
的主键。每个实体对象都需要一个唯一标识,@Id
用来表明哪个字段是主键。
4. @GeneratedValue(strategy = GenerationType.IDENTITY)
- 作用:定义主键的生成策略。
- 解释:
- 使用 自增策略(
IDENTITY
生成方式),即数据库会自动生成主键值(如 MySQL 的自增主键)。 - 每次插入新数据时,
id
字段的值会由数据库自动递增。
- 使用 自增策略(
5. @Column(name = "user_id")
- 作用:将实体类的字段映射到数据库表中的列,并指定列名。
- 解释:
id
字段映射到数据库表tb_user
中的user_id
列。- 如果不加
@Column
注解,JPA 会默认将字段名与列名一致(如字段名为id
,则列名也为id
)。
6. @Column(name = "user_name")
- 作用:将
name
字段映射到数据库表中的user_name
列。
7. @Column(name = "user_password")
- 作用:将
password
字段映射到数据库表中的user_password
列。
8. @Column(name = "user_email")
- 作用:将
email
字段映射到数据库表中的user_email
列。
代码的注解功能:
-
类级别注解:
@Entity
:将类标记为 JPA 实体类。@Table(name = "tb_user")
:将类与数据库表tb_user
进行映射。
-
字段级别注解:
@Id
:标识主键字段。@GeneratedValue(strategy = GenerationType.IDENTITY)
:主键使用数据库的自增策略。@Column(name = "xxx")
:将字段映射到数据库表的指定列名。
通过这些注解,JPA 框架可以自动将类 User
与数据库表 tb_user
关联起来,并根据类的字段与数据库列之间的映射关系,帮助开发者进行数据库操作(如增删改查)。
备注
标记为 JPA 实体类的作用
在 Java Persistence API(JPA)中,将类标记为实体类(通过 @Entity
注解)有以下作用:
1. 将类映射为数据库表
@Entity
告诉 JPA 框架,这个类是一个 实体类,需要与数据库中的表进行映射。- 每个实体类的实例(对象)对应数据库表中的一行数据。
- 类的字段(属性)通过注解(如
@Column
)映射到数据库表的列。
示例:
@Entity
@Table(name = "tb_user")
public class User {
@Id
private Integer id; // 对应 tb_user 表的主键列
private String name; // 对应 tb_user 表中的 name 列
}
总结
将类标记为 JPA 实体类的作用主要体现在以下几个方面:
- 映射类与数据库表的关系:通过注解定义类与表、字段与列的映射规则。
- 启用 ORM 功能:实现对象与数据库记录的自动转换,简化数据操作。
- 支持持久化操作:通过 JPA 的 API 或框架(如 Spring Data JPA)操作数据库,而无需手写 SQL。
- 与数据库表结构集成:支持自动生成或更新表结构,便于维护。
- 增强功能支持:如关联关系管理、懒加载、级联操作等。
controller
这里我们写好了用于各个层之间交换的数据结构后,我们就开始正式进行后端项目的功能的编写。这里我们从controller层开始写。我们在这里只需要使用对数据库的增删改查功能:
package com.jiaowu.backend.controller;
import com.jiaowu.backend.pojo.ResponseMessage;
import com.jiaowu.backend.pojo.User;
import com.jiaowu.backend.pojo.dto.UserDto;
import com.jiaowu.backend.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController //允许接口方法返回对象,转换成json文本
@RequestMapping("/user") // localhost:8080/user/**
public class UserController {
@Autowired
IUserService userService;
//REST——请求方法
//增加
@PostMapping // localhost:8080/user method:post
public ResponseMessage<User> addUser(@Validated @RequestBody UserDto user) {
User userNew = userService.add(user);
return ResponseMessage.success(userNew);
}
//查询
@GetMapping("/{userId}") // localhost:8080/user/{userId} method:get
public ResponseMessage<User> get(@PathVariable Integer userId) {
User userNew = userService.getUser(userId);
return ResponseMessage.success(userNew);
}
@GetMapping("/all")
public ResponseMessage<User> getAll() {
List<User> userNew = userService.getAll();
return ResponseMessage.success(userNew);
}
//修改
@PutMapping
public ResponseMessage<User> edit(@Validated @RequestBody UserDto userId) {
User userNew = userService.edit(userId);
return ResponseMessage.success(userNew);
}
//删除
@DeleteMapping("/{userId}")
public ResponseMessage<User> delete(@PathVariable Integer userId) {
userService.delete(userId);
return ResponseMessage.success();
}
}
代码注释总结
以下是代码中各注释的作用与含义:
类级别注解
1. @RestController
- 作用:标识该类为一个 RESTful 控制器。
- 功能:允许接口方法直接返回对象,Spring 会将对象自动转换为 JSON 格式的响应。
2. @RequestMapping("/user")
- 作用:为该控制器类定义一个基础 URL 前缀。
- 功能:所有方法的 URL 都以
/user
开头,例如:localhost:8080/user
成员变量注解
3. @Autowired
- 作用:自动注入
IUserService
接口的实现类。 - 功能:使
userService
变量可以直接调用服务层的逻辑。
方法级别注解
增加用户
4. @PostMapping
- 作用:映射 HTTP POST 请求。
- 功能:处理
localhost:8080/user
的 POST 请求,用于新增用户。
5. @Validated
- 作用:启用数据校验。
- 功能:对请求体中的
UserDto
对象进行校验,例如检查字段的格式或必填项。
6. @RequestBody
- 作用:将 HTTP 请求体的 JSON 数据反序列化为 Java 对象。
- 功能:将客户端发送的用户数据解析为
UserDto
对象。
查询用户
7. @GetMapping("/{userId}")
- 作用:映射 HTTP GET 请求。
- 功能:处理
localhost:8080/user/{userId}
的 GET 请求,用于根据用户 ID 查询用户。
8. @PathVariable
- 作用:将 URL 中的动态路径变量绑定到方法参数。
- 功能:将
{userId}
解析为方法的userId
参数。
9. @GetMapping("/all")
- 作用:映射 HTTP GET 请求。
- 功能:处理
localhost:8080/user/all
的 GET 请求,返回所有用户信息。
修改用户
10. @PutMapping
- 作用:映射 HTTP PUT 请求。
- 功能:处理
localhost:8080/user
的 PUT 请求,用于修改用户信息。
删除用户
11. @DeleteMapping("/{userId}")
- 作用:映射 HTTP DELETE 请求。
- 功能:处理
localhost:8080/user/{userId}
的 DELETE 请求,用于根据用户 ID 删除用户。
方法返回值说明
12. 返回值类型
- 类型:
ResponseMessage<User>
- 作用:封装接口的响应结果,通常包括:
success
:请求是否成功。data
:具体返回的数据(如用户对象或用户列表)。message
:提示信息。
总结
代码功能
- 控制器类:定义了与用户相关的 RESTful 接口,包括新增、查询、修改、删除用户等操作。
- 注解使用:通过 Spring MVC 注解(如
@RestController
、@PostMapping
、@GetMapping
等)简化了接口开发,清晰地定义了请求的类型和 URL 路径。 - 数据流:
- 客户端发送 JSON 请求。
- 通过
@RequestBody
反序列化为 Java 对象。 - 调用服务层逻辑处理数据。
- 最终返回 JSON 格式的响应。
RESTful 接口一览
请求方法 | URL | 描述 |
---|---|---|
POST | /user | 新增用户 |
GET | /user/{userId} | 查询用户 |
GET | /user/all | 查询所有用户 |
PUT | /user | 修改用户 |
DELETE | /user/{userId} | 删除用户 |
repository
这里我们实现数据访问层:
package com.jiaowu.backend.repository;
import com.jiaowu.backend.pojo.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends CrudRepository<User, Integer> {
}
代码分析与注释总结
以下是代码中注释的作用与详细解释:
1. @Repository
- 作用:将接口标记为一个 Spring 数据访问层组件。
- 功能:
- 表示当前接口是一个 DAO(数据访问对象),负责与数据库交互。
- Spring 会将该接口的实现类自动注册为 Bean,并纳入到 Spring 容器中管理。
- 提供数据访问相关的异常转换功能(将底层的数据库异常转换为 Spring 的统一异常,如
DataAccessException
)。
2. public interface UserRepository extends CrudRepository<User, Integer>
- 作用:定义一个 JPA Repository,用于对
User
实体进行 CRUD(增删改查)操作。 - 解释:
UserRepository
:- 是一个接口,继承了 Spring Data 提供的
CrudRepository
接口。 - 不需要手动实现,Spring 会自动生成实现类。
- 是一个接口,继承了 Spring Data 提供的
CrudRepository<User, Integer>
:- 泛型参数解析:
User
:指定该仓库要操作的实体类类型。Integer
:指定主键的类型(id
的数据类型是Integer
)。
- 泛型参数解析:
3. CrudRepository
的功能
CrudRepository
是 Spring Data JPA 提供的一个基础接口,包含了常用的 CRUD 操作方法。通过继承 CrudRepository
,可以直接使用这些方法,无需额外实现。
service
编写业务逻辑层:
package com.jiaowu.backend.service;
import com.jiaowu.backend.pojo.User;
import com.jiaowu.backend.pojo.dto.UserDto;
import com.jiaowu.backend.repository.UserRepository;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service // spring bean
public class UserService implements IUserService {
@Autowired
UserRepository userRepository;
@Override
public User add(UserDto user) {
User userPojo = new User();
BeanUtils.copyProperties(user, userPojo);
return userRepository.save(userPojo);
}
@Override
public User getUser(Integer userId) {
return userRepository.findById(userId).orElseThrow(() -> {
throw new IllegalArgumentException("用户不存在,参数异常");
});
}
@Override
public User edit(UserDto user) {
User userPojo = new User();
BeanUtils.copyProperties(user, userPojo);
return userRepository.save(userPojo);
}
@Override
public void delete(Integer userId) {
userRepository.deleteById(userId);
}
@Override
public List<User> getAll() {
return (List<User>)userRepository.findAll();
}
}
代码分析与注释总结
以下是代码中注释的作用与详细解释:
1. 类级别注解
@Service
- 作用:将该类标记为一个 Spring 服务层组件。
- 功能:
- 表示该类是业务逻辑层的组件(Service Bean),由 Spring 容器管理。
- 通过依赖注入(如
@Autowired
),可以在控制器或其他组件中调用服务层逻辑。
2. 成员变量注解
@Autowired
- 作用:对
UserRepository
进行自动注入。 - 功能:
- Spring 容器会将
UserRepository
的实现类(由 Spring Data JPA 自动生成的代理类)注入到userRepository
中。 - 用于提供数据库操作方法(如
save
、findById
、deleteById
等)。
- Spring 容器会将
3. 方法实现分析
add(UserDto user)
-
作用:新增一个用户。
-
功能:
- 创建一个空的
User
对象(实体类)。 - 使用
BeanUtils.copyProperties
复制UserDto
对象的属性值到User
对象中。 - 调用
userRepository.save()
将用户数据保存到数据库。 - 返回保存后的
User
对象。
- 创建一个空的
-
关键点:
BeanUtils.copyProperties(source, target)
:- Spring 提供的工具类,用于将一个对象的属性值快速复制到另一个对象中。
- 需要确保属性名称和类型一致。
userRepository.save()
:- Spring Data JPA 提供的方法,用于新增或更新数据库记录。
- 如果实体没有主键值,则新增;如果有主键值,则更新。
getUser(Integer userId)
-
作用:根据用户 ID 查询用户。
-
功能:
- 调用
userRepository.findById(userId)
查询用户。 - 如果用户不存在,则抛出
IllegalArgumentException
,提示用户不存在。 - 如果用户存在,返回查询到的
User
对象。
- 调用
-
关键点:
findById(userId)
:- 返回一个
Optional
对象,避免直接返回null
。
- 返回一个
.orElseThrow()
:- 当
Optional
中没有值时,抛出指定的异常。
- 当
edit(UserDto user)
-
作用:编辑用户信息。
-
功能:
- 创建一个空的
User
对象。 - 使用
BeanUtils.copyProperties
将UserDto
的属性复制到User
对象中。 - 调用
userRepository.save()
更新用户信息。 - 返回更新后的
User
对象。
- 创建一个空的
-
关键点:
- 编辑和新增的逻辑非常相似,区别在于:
- 新增时,主键值为
null
,会新增一条记录。 - 编辑时,需要确保传入的对象有主键,否则会新增而不是更新。
- 新增时,主键值为
- 编辑和新增的逻辑非常相似,区别在于:
delete(Integer userId)
-
作用:根据用户 ID 删除用户。
-
功能:
- 调用
userRepository.deleteById(userId)
删除指定用户。
- 调用
-
关键点:
deleteById(userId)
:- Spring Data JPA 提供的方法,根据主键删除对应的记录。
- 如果记录不存在,不会抛出异常(取决于底层数据库实现)。
getAll()
-
作用:查询所有用户。
-
功能:
- 调用
userRepository.findAll()
查询所有用户。 - 将结果转换为
List<User>
并返回。
- 调用
-
关键点:
findAll()
:- 返回一个
Iterable
,可以通过(List<User>)
强制转换为列表。
- 返回一个
- 强制转换是因为
CrudRepository
默认返回Iterable
,而业务通常需要List
。
4. 代码逻辑总结
方法功能一览
方法名称 | 功能描述 |
---|---|
add(UserDto user) | 新增一个用户,将 UserDto 转换为 User 并保存。 |
getUser(Integer userId) | 根据用户 ID 查询用户,不存在则抛出异常。 |
edit(UserDto user) | 编辑用户信息,将传入的 UserDto 更新到数据库中。 |
delete(Integer userId) | 根据用户 ID 删除用户。 |
getAll() | 查询所有用户,返回用户列表。 |
5. 注意点
-
BeanUtils.copyProperties
的使用:- 简化了对象之间的属性赋值,但需要注意:
- 属性名和类型必须一致,否则会跳过不匹配的字段。
- 可能会忽略嵌套对象的处理(例如复杂对象内的字段不会自动复制)。
- 简化了对象之间的属性赋值,但需要注意:
-
异常处理:
- 在
getUser
方法中,使用orElseThrow
抛出异常,保证调用方法时不会出现空指针问题。 - 可以根据业务需求,定义自定义异常类代替
IllegalArgumentException
,提高可读性。
- 在
-
数据类型转换:
getAll
方法中,findAll
返回的是Iterable
,需要手动转换为List
。- 强制类型转换可能会出现问题,建议直接用
Stream
或工具类进行转换。
为什么在业务开发中 Service 层需要定义一个接口?
在业务开发中,通常会为 Service 层定义一个接口(如 IUserService
),然后通过实现类(如 UserService
)实现该接口。这种设计并不是强制性的,但它遵循了良好的软件设计原则,带来了许多实际的好处。
1. 遵循面向接口编程的原则
- 面向接口编程 是软件设计中的重要原则,强调程序应依赖于抽象(接口),而不是具体的实现类。
- 好处:
- 服务调用者(控制器或其他服务)只需要知道接口的方法,而不需要关心具体实现。
- 使代码更加灵活和解耦,方便后续拓展和维护。
dto
不难发现,我们在编写代码的时候,发现使用到了一个中间变量, 也就是还是用于传输的变量,以下是定义:
package com.jiaowu.backend.pojo.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
public class UserDto {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
private Integer id;
@NotBlank(message = "用户名不能为空")
private String name;
@NotBlank(message = "密码不能为空")
@Length(min = 6,max = 12)
private String password;
@Email(message = "邮箱格式不正确")
private String email;
@Override
public String toString() {
return "UserDto{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
1. 简介
UserDto
是一个 数据传输对象(Data Transfer Object, DTO),用于在客户端和服务端之间传递用户数据。
DTO 的主要职责是:
- 封装用户的请求数据(如新增或编辑用户时的输入)。
- 提供输入验证功能,确保数据的合法性。
2. 字段分析
1. private Integer id
- 解释:
id
是用户的唯一标识,用于编辑或删除用户时的主键。 - 注意:在新增用户时,通常不需要传递
id
,此字段在某些场景下可能为空(如新增用户)。
2. @NotBlank(message = "用户名不能为空")
- 作用:
- 用于验证
name
字段不能为空或全是空格。 - 如果用户未提供
name
,会抛出验证异常,返回自定义错误信息。
- 用于验证
- 适用场景:新增或编辑用户时,用户名为必填项。
3. @NotBlank(message = "密码不能为空")
- 作用:
- 验证
password
字段不能为空或全是空格。
- 验证
- 额外验证:
@Length(min = 6, max = 12)
- 限制密码长度,必须在 6~12 个字符之间。
- 适用场景:新增用户或更新密码时。
4. @Email(message = "邮箱格式不正确")
- 作用:
- 验证
email
是否符合邮箱格式。 - 如果用户提供的邮箱格式不正确,则返回错误信息。
- 验证
- 适用场景:新增或编辑用户时。
字段验证总结
字段名 | 验证注解 | 描述 |
---|---|---|
id | 无 | 主键字段,可为空(新增时)。 |
name | @NotBlank | 用户名不能为空或全是空格。 |
password | @NotBlank + @Length(6, 12) | 密码不能为空,长度必须在 6~12 个字符之间。 |
email | @Email | 检查邮箱格式是否合法。 |
3. 常用方法分析
Getter 和 Setter 方法
- 作用:提供对私有字段的访问和修改。
- 优点:
- 提供封装,保护字段的直接访问。
- 允许对字段的访问进行控制(如添加逻辑验证)。
- 改进建议:
- 可使用 Lombok 简化代码,自动生成
Getter
和Setter
方法,减少代码冗余。
- 可使用 Lombok 简化代码,自动生成
toString
方法
- 作用:
- 重写了
toString
方法,用于输出对象的字符串表示。 - 在调试或日志打印时,便于查看对象内容。
- 重写了
- 改进建议:
- 使用 Lombok 的
@ToString
注解,简化代码。
- 使用 Lombok 的
为什么要使用dto的变量而不是直接使用原来的user呢?
在开发中,DTO
(数据传输对象,Data Transfer Object)常用于 数据传输,特别是在控制器层(Controller)和服务层(Service)之间传递数据,而不是直接使用实体类(如 User
)。
这种设计是一种 分层架构的最佳实践,能够提高代码的灵活性、安全性和可维护性。以下是详细原因和优点:
1. 职责分离
实体类的职责
- 实体类(如
User
)通常是直接与数据库表映射的类(通过 JPA 或 Hibernate 等 ORM 框架)。 - 它的主要职责是反映数据库结构(如字段)和处理持久化逻辑。
DTO 的职责
- DTO 的职责是用来封装和传递客户端请求或响应的 数据结构。
- 它关注的是 数据传输,而不是数据库操作或业务逻辑。
好处:
- 分离了数据传输逻辑和持久化逻辑。
- 使 数据库模型 和 数据传输格式 之间解耦,避免直接暴露数据库模型的结构。
2. 避免暴露实体类的敏感信息
实体类中可能包含一些敏感信息或不需要暴露给客户端的数据(如密码的哈希值、数据库主键等)。
直接使用实体类会导致以下问题:
- 无法控制哪些字段可以被客户端访问或修改。
- 如果实体类字段较多,可能会导致不必要的数据暴露。
统一异常处理
后端处理出现了异常的时候,通常是不用将这个异常情况反馈给前端的,所以我们这里可以定义一个统一的异常处理类来进行返回统一异常信息:
package com.jiaowu.backend.exception;
import com.jiaowu.backend.pojo.ResponseMessage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice //统一处理
public class GlobalExceptionHanderAdvice {
// 记录日志
Logger log = LoggerFactory.getLogger(GlobalExceptionHanderAdvice.class);
@ExceptionHandler({Exception.class})
public ResponseMessage handleException(Exception e, HttpServletRequest request, HttpServletResponse response) {
log.error(e.getMessage(), e);
return new ResponseMessage(500,"error",null);
}
}
简介
功能
GlobalExceptionHanderAdvice
是一个全局异常处理类,用于捕获和处理应用程序中的异常。- 通过使用
@RestControllerAdvice
注解,可以拦截所有控制器方法中抛出的异常(包括运行时异常),并进行统一的处理。
为什么需要全局异常处理?
- 提高代码的可维护性和一致性:避免在每个控制器中重复编写异常处理逻辑。
- 提供用户友好的错误响应:统一异常格式,便于前端解析和展示。
- 记录详细的异常信息:通过日志记录系统错误,便于后续排查问题。
2. 代码功能分析
1. @RestControllerAdvice
- 作用:这是 Spring 提供的增强功能,结合
@ExceptionHandler
注解,可以对所有@RestController
抛出的异常进行拦截和处理。 - 特点:
- 默认会拦截所有 REST API 请求中的异常。
- 返回的对象会直接转化为 JSON 响应,适合 RESTful 风格的接口。
2. @ExceptionHandler({Exception.class})
- 作用:指定处理的异常类型,此处捕获所有异常(包括运行时异常)。
- 逻辑:
- 记录异常日志:通过
SLF4J
的Logger
记录完整的异常堆栈信息。 - 返回统一的错误响应:构造
ResponseMessage
对象作为标准的错误响应。
- 记录异常日志:通过
3. 日志记录
Logger log = LoggerFactory.getLogger(GlobalExceptionHanderAdvice.class)
- 通过 SLF4J 记录异常日志。
log.error(e.getMessage(), e)
:- 输出异常的详细信息,包括错误消息和堆栈跟踪,便于排查问题。
4. 返回对象:ResponseMessage
ResponseMessage
是一个封装类,用于统一接口的响应格式。- 返回值示例:
{ "status": 500, "message": "error", "data": null }
小结
这里我们将SpringBoot框架的基本操作都做了一遍,不难看出,springboot开发需要用到大量的注释,省去了开发人员的编写麻烦问题。