Spring常用注解总结(持续更新)

0. 引言

因为我的个人习惯,我会从: 是什么(What),为什么(Why),怎么用(How)三个方面来分析这些技术和知识点,便于我加深理解和记忆。

注解名称 注解作用
@SpringBootApplication Spring Boot 的核心组合注解,包含配置、自动装配和组件扫描。
@Autowired Spring 的自动依赖注入注解。
@Component 通用组件。
@Repository 数据库层。
@Service 业务逻辑层。
@Controller 接口层(Web 层)。
@RestController 把一个类标记为 控制器(Controller) ,并且该类下所有方法默认都以 JSON / XML 等格式返回数据,而不是返回视图(JSP/Thymeleaf 等)。
@Scope Spring 提供的一个 Bean 作用域(Scope)声明注解。 (单例/多例)
@Component 用来声明配置类,里面的方法用 @Bean 定义 Bean。
@PathVariable 用于 URL 路径变量(资源定位)。
@RequestParam 用于 URL 查询参数表单参数(条件、筛选)。
@RequestBody 用来把请求体中的 JSON 数据转成 Java 对象,特别适合前后端分离的场景。
@Value 读取单个配置属性。
@ConfigurationProperties 将配置文件中的属性绑定到一个 Java Bean 上,支持嵌套对象和集合。
@PropertySource 指定读取某个 properties 文件(不常用,多用于老项目)。
@Null 被注释的元素必须为 null
@NotNull 不能为 null
@NotEmpty 字符串或集合不能为空
@NotBlank 字符串不能为空且必须有非空白字符
@Email 必须是合法邮箱
@Size(min=, max=) 字符串或集合长度/大小限制
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Pattern(regex=,flag=) 必须符合指定正则
@Min/@Max 数值最小/最大值
@DecimalMin/@DecimalMax 小数最小/最大值
@Past/@Future 日期必须为过去/将来
@AssertTrue/@AssertFalse 布尔值必须为 true/false
@Transactional 事务
  • 各种主键生成策略对照表
策略名 对应类 说明 适用场景
uuid2 UUIDGenerator 使用 Java UUID v2 生成 推荐,分布式环境
guid GUIDGenerator GUID,全局唯一标识 Windows/SQL Server
uuid / uuid.hex UUIDHexGenerator 旧版 UUID 已废弃
assigned Assigned 主键由应用代码手动赋值 业务系统已有主键
identity IdentityGenerator 数据库自增主键 MySQL 常用
sequence SequenceStyleGenerator 数据库序列 Oracle/PostgreSQL
seqhilo SequenceHiLoGenerator hi/lo 算法生成主键 大并发优化
increment IncrementGenerator 自增计数器 单机测试用,不推荐生产
foreign ForeignGenerator 使用外键作为主键 一对一关系
sequence-identity SequenceIdentityGenerator 序列 + 自增 特殊数据库
enhanced-sequence SequenceStyleGenerator 增强版序列 替代旧 sequence
enhanced-table TableGenerator 通过表维护主键 无序列支持的数据库
  • JPA 常用注解速查表
分类 注解 说明
表相关 @Entity 标记一个类为 JPA 实体类,映射数据库表
@Table(name = "xxx") 指定实体类对应的表名(默认类名)
@Column(name = "xxx", nullable = false, length = 100) 指定字段名、长度、是否可空等约束
@Id 标识 主键字段
主键策略 @GeneratedValue(strategy = GenerationType.IDENTITY) 自增主键(MySQL 常用)
@GeneratedValue(strategy = GenerationType.SEQUENCE) 使用数据库序列生成主键(Oracle 常用)
@GeneratedValue(strategy = GenerationType.UUID) 使用 UUID 作为主键(Spring Boot 3.0+ 支持)
字段控制 @Transient 该字段 不持久化(不会映射到表)
@Lob 存储大字段(如 TEXTBLOB
@Enumerated(EnumType.STRING) 枚举保存为 字符串
@Enumerated(EnumType.ORDINAL) 枚举保存为 序号
审计功能 @EnableJpaAuditing 开启 JPA 审计功能(放在配置类上)
@CreatedDate 自动填充 创建时间
@LastModifiedDate 自动填充 修改时间
@CreatedBy 自动填充 创建人
@LastModifiedBy 自动填充 修改人
修改删除 @Modifying 标记 更新/删除 SQL 方法(配合 @Query
@Transactional 开启事务,保证修改/删除的原子性
关联关系 @OneToOne 一对一关系(如用户与身份证)
@OneToMany 一对多关系(如用户与订单)
@ManyToOne 多对一关系(如订单属于用户)
@ManyToMany 多对多关系(如学生与课程)

一. @SpringBootApplication

1. 是什么(What)

  • @SpringBootApplication 是 Spring Boot 提供的一个 核心注解

  • 它是一个 组合注解,实际上包含了三个常用注解:

less 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration   // 表示这是一个配置类,相当于@Configuration
@EnableAutoConfiguration   // 启动Spring Boot的自动配置功能
@ComponentScan             // 开启组件扫描,扫描当前包及子包下的Bean
public @interface SpringBootApplication {
}
  • 也就是说:@SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan

2. 为什么(Why)

为什么要用 @SpringBootApplication?原因有三个:

  • 简化配置

    • 过去写 Spring 程序,需要在启动类上写多个注解,比如:
less 复制代码
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class AppConfig { ... }
  • Spring Boot 把这些合并成一个 @SpringBootApplication,让启动类更简洁。

  • 根据 SpringBoot 官网,这三个注解的作用分别是:

    • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
    • @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类。
    • @Configuration:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类
  • 统一入口

    • 所有 Spring Boot 应用都有一个 入口类 (main 方法所在的类),加上 @SpringBootApplication 就能告诉 Spring Boot:

      "从我这里开始启动应用,自动配置并扫描组件"。

  • 约定大于配置

    • @EnableAutoConfiguration 会根据 classpath 里的依赖和配置,自动装配 Spring Bean(例如加了 spring-boot-starter-web 就自动配置 Tomcat 和 SpringMVC)。
    • 开发者只需要 专注业务逻辑,不用手动写繁琐的配置。

3.怎么用(How)

一般用法很简单:

  • 在入口类上加注解
typescript 复制代码
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
  • 控制扫描范围(可选)

    默认情况下,@ComponentScan 会扫描入口类所在包及其子包。

    如果项目包结构复杂,可以手动指定:

    kotlin 复制代码
    @SpringBootApplication(scanBasePackages = "com.example.project")
    public class MyApplication { ... }
  • 排除某些自动配置(可选)

    如果某些自动配置和你的业务冲突,可以排除:

    kotlin 复制代码
    @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
    public class MyApplication { ... }

4.总结

  • 是什么@SpringBootApplication 是 Spring Boot 的核心组合注解,包含配置、自动装配和组件扫描。

  • 为什么:让项目入口简洁,减少配置,利用自动化机制提升开发效率。

  • 怎么用 :加在应用主类上,main 方法调用 SpringApplication.run(...),可选指定扫描范围和排除配置。

二. Spring Bean 相关

1. @Autowired

1.1、是什么(What)

  • @AutowiredSpring 框架 提供的一个 依赖注入(Dependency Injection, DI)注解
  • 它的作用是:自动装配 Bean ------ Spring 容器会根据类型(byType)自动找到合适的 Bean,并注入到标注了 @Autowired 的变量、构造方法或 setter 方法中。

换句话说:@Autowired 就是告诉 Spring: "请帮我找一个合适的 Bean 注入到这里"


1.2、为什么(Why)

为什么要用 @Autowired 而不是自己 new 对象?原因有三个:

  • 降低耦合度

    • 如果你手动 new 对象,代码强依赖具体实现。
    • @Autowired,对象由 Spring 容器统一管理,方便替换和扩展。
  • 依赖注入自动化

    • 传统 XML 配置需要 <bean> 配置和 <property ref="xxx"> 注入,非常繁琐。
    • @Autowired 可以省去大量配置,提升开发效率。
  • 支持多种注入方式

    • 支持 字段注入、构造器注入、Setter 方法注入,灵活方便。

1.3、怎么用(How)

常见用法有三种:

1.3.1. 字段注入(最常见,但不推荐在大项目中)
typescript 复制代码
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void doSomething() {
        userRepository.save(...);
    }
}
1.3.2. 构造器注入(推荐,最安全,利于单元测试)
kotlin 复制代码
@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired  // Spring 5+ 单构造器时可以省略
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
1.3.3. Setter 注入(用于可选依赖)
typescript 复制代码
@Service
public class UserService {
    private NotificationService notificationService;

    @Autowired(required = false)  // 表示注入可选
    public void setNotificationService(NotificationService notificationService) {
        this.notificationService = notificationService;
    }
}
1.3.4. 自动导入对象到类中,被注入进的类同样要被 Spring 容器管理

比如:Service 类注入到 Controller 类中。

less 复制代码
@Service  
public class UserService {  
  ......  
}  
  
@RestController  
@RequestMapping("/users")  
public class UserController {  
   @Autowired  
   private UserService userService;  
   ......  
}

1.4、细节知识点

  • 默认按类型装配

    • Spring 会根据依赖的类型(class/interface)去容器中找唯一匹配的 Bean。
  • 多个候选 Bean 时怎么办?

    • 如果有多个候选 Bean,会报错。解决方法:

      • @Qualifier("beanName") 指定 Bean 名称。
      • 或者给其中一个 Bean 加 @Primary
    less 复制代码
    @Service("emailService")
    public class EmailNotificationService implements NotificationService {}
    
    @Service("smsService")
    public class SmsNotificationService implements NotificationService {}
    
    @Service
    public class UserService {
        @Autowired
        @Qualifier("emailService")
        private NotificationService notificationService;
    }
  • 和 @Resource 的区别

    • @Autowired 默认按 类型 注入。
    • @Resource 默认按 名称 注入。

1.5、总结

  • 是什么@Autowired 是 Spring 的自动依赖注入注解。
  • 为什么:减少手动创建对象,降低耦合,让 Bean 管理更灵活。
  • 怎么用 :加在字段、构造器、setter 上,默认按类型注入,必要时配合 @Qualifier@Primary

2.Component,@Repository,@Service, @Controller

2.1、是什么(What)

这四个注解都是 Spring 的组件注解 ,用于把类注册到 Spring 容器中,变成一个 Bean

  • @Component

    • 最基础的组件注解。
    • 表示"这是一个 Spring 管理的 Bean",通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。。
  • @Repository

    • 作用在 DAO(数据访问层) ,比如操作数据库的类。
    • @Component 基础上扩展了 持久层异常转换功能(把 JDBC 异常转成 Spring 的统一异常)。
  • @Service

    • 作用在 Service(业务逻辑层)
    • 本质上还是 @Component,但语义更清晰,表示这是业务逻辑类。
  • @Controller

    • 作用在 Controller(表现层 / Web 层)
    • 搭配 @RequestMapping@GetMapping@PostMapping 等注解,用来处理 Web 请求,接受用户请求并调用 Service 层返回数据给前端页面。。

✅ 本质:这四个注解都是 @Component 的衍生注解,只是为了分层更清晰。


2.2、为什么(Why)

为什么要分成四个注解?

  1. 分层语义更清晰

    • @Component:通用组件。
    • @Repository:数据库层。
    • @Service:业务逻辑层。
    • @Controller:接口层(Web 层)。
      这样别人一看就能知道类的用途。
  2. 支持 AOP 扩展

    • Spring 可以根据注解识别不同层的 Bean,应用不同的切面逻辑:

      • @Repository:提供数据访问异常的转换。
      • @Service:常用于事务管理。
      • @Controller:支持 MVC 请求处理。
  3. 更好的维护性

    • 大型项目中代码多,分层注解能让团队协作时更直观。

2.3、怎么用(How)

2.3.1. @Component(通用组件)
csharp 复制代码
@Component
public class MyHelper {
    public void doWork() {
        System.out.println("Doing helper work...");
    }
}
2.3.2. @Repository(数据访问层)
kotlin 复制代码
@Repository
public class UserRepository {
    public User findById(Long id) {
        // 假装去数据库查询
        return new User(id, "张三");
    }
}
2.3.3. @Service(业务逻辑层)
kotlin 复制代码
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public User getUser(Long id) {
        return userRepository.findById(id);
    }
}
2.3.4. @Controller(表现层 / Web 层)
less 复制代码
@Controller
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/user/{id}")
    @ResponseBody
    public User getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }
}

2.4、对比总结表

注解 所属层 本质 额外功能/语义 使用场景
@Component 通用 Spring Bean 工具类、通用组件
@Repository DAO 层 @Component 异常转换 数据访问层
@Service Service 层 @Component 语义标识 业务逻辑层
@Controller Web 层 @Component 请求处理能力 接口层,接收/响应请求

2.5、总结

  • @Component:最基础的 Bean 注解。
  • @Repository、@Service、@Controller :语义化的 @Component,分别用于 DAO 层、Service 层、Controller 层,让分层更清晰,同时方便 Spring AOP 和 MVC 扩展。

3.@RestController

3.1、是什么(What)

  • @RestController 是 Spring 4 之后新增的注解。

  • 它是一个 组合注解

    less 复制代码
    @Controller
    @ResponseBody
    public @interface RestController {}
  • 也就是说:@RestController = @Controller + @ResponseBody

  • 作用:把一个类标记为 控制器(Controller) ,并且该类下所有方法默认都以 JSON / XML 等格式返回数据,而不是返回视图(JSP/Thymeleaf 等)。


3.2、为什么(Why)

为什么要用 @RestController

  1. 简化开发

    • 以前如果要返回 JSON,需要写:

      less 复制代码
      @Controller
      public class UserController {
          @GetMapping("/user")
          @ResponseBody
          public User getUser() { ... }
      }
    • 每个方法都得加 @ResponseBody,很麻烦。

    • @RestController 之后:

      kotlin 复制代码
      @RestController
      public class UserController {
          @GetMapping("/user")
          public User getUser() { ... }
      }

      ✅ 默认所有方法返回 JSON,更简洁。

  2. 契合前后端分离

    • 现在的项目基本都是 前后端分离,前端需要 JSON 数据,而不是 HTML 页面。
    • @RestController 就是专门为这种 RESTful 风格的接口准备的。
  3. 和 @Controller 的区别

    • @Controller 用于传统 MVC,方法返回 视图名称(如 JSP 页面)。
    • @RestController 用于 REST API,方法返回 数据对象(Spring 会自动转成 JSON/XML)。

3.3、怎么用(How)

3.3.1. 最简单的示例
kotlin 复制代码
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String sayHello() {
        return "Hello, Spring Boot!";
    }
}

访问 /hello,浏览器看到的不是页面,而是字符串 "Hello, Spring Boot!"

3.3.2. 返回对象(Spring 会自动转 JSON)
less 复制代码
@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return new User(id, "张三");
    }
}

返回结果(JSON):

json 复制代码
{
  "id": 1,
  "name": "张三"
}
3.3.3. 搭配 @RequestBody 接收 JSON 参数
less 复制代码
@PostMapping
public User createUser(@RequestBody User user) {
    // 假装保存用户
    return user;
}

发送 JSON 请求:

json 复制代码
{ "id": 2, "name": "李四" }

返回结果:

json 复制代码
{ "id": 2, "name": "李四" }

3.4、总结对比表

注解 用途 返回结果 典型场景
@Controller Web 控制器 视图(HTML/JSP/Thymeleaf) 传统 MVC
@RestController REST 控制器 JSON/XML(默认 JSON) RESTful API / 前后端分离

3.5、总结

  • @RestController = @Controller + @ResponseBody

  • 它让控制器方法默认返回 JSON 数据 ,更适合 前后端分离RESTful API 项目开发。

4.@Scope

4.1、是什么(What)

  • @Scope 是 Spring 提供的一个 Bean 作用域(Scope)声明注解
  • 它用来指定 Spring 容器中 Bean 的生命周期和作用范围
  • 可以标注在类上(和 @Component@Service 等一起用),也可以标注在 @Bean 方法上。

常见作用域值有:

作用域值 含义 使用场景
singleton(默认) 单例:整个 Spring 容器中只存在一个该 Bean 实例 大部分业务 Bean(Service、Dao)
prototype 多例:每次获取都会创建一个新的实例 有状态的 Bean,比如多线程任务对象
request 每个 HTTP 请求创建一个 Bean,request 结束后销毁 Web 应用中,保存一次请求的数据
session 每个 HTTP 会话创建一个 Bean,session 结束后销毁 Web 应用中,保存用户会话数据
application 每个 ServletContext 一个 Bean 跨会话共享数据(类似单例,但作用于 Web 应用)
websocket 每个 WebSocket 会话一个 Bean WebSocket 应用场景

4.2、为什么(Why)

为什么要用 @Scope

  1. 控制 Bean 的生命周期

    • 有些对象适合全局共享(比如 Service、Repository) → 单例。
    • 有些对象必须每次都新建(比如用户输入表单对象) → 多例。
  2. 适配不同的 Web 应用场景

    • 请求级别的 Bean(request),能避免用户数据交叉污染。
    • 会话级别的 Bean(session),方便保存用户状态。
  3. 灵活管理资源

    • 单例节省内存,但不适合存储用户特有的数据。
    • 多例虽然更灵活,但会增加内存开销。
    • @Scope 让开发者根据场景选择合适的生命周期。

4.3、怎么用(How)

4.3.1. 基本用法
less 复制代码
@Component
@Scope("prototype")
public class MyBean {
    public MyBean() {
        System.out.println("创建了一个 MyBean 实例");
    }
}
  • 每次 context.getBean(MyBean.class) 都会创建一个新的对象。
4.3.2. 在 @Bean 方法上用
less 复制代码
@Configuration
public class AppConfig {
    @Bean
    @Scope("singleton")  // 默认值,可以省略
    public UserService userService() {
        return new UserService();
    }
}
4.3.3. Web 应用中的作用域
less 复制代码
@Component
@Scope("request")
public class RequestScopedBean {
    // 每个 HTTP 请求都会创建新的实例
}
less 复制代码
@Component
@Scope("session")
public class SessionScopedBean {
    // 每个用户会话对应一个实例
}

4.4、总结表

作用域 生命周期 典型场景
singleton(默认) Spring 容器启动时创建,容器销毁时销毁 大多数 Service、Dao
prototype 每次获取都创建 多线程任务、临时对象
request 每次 HTTP 请求新建 Web 应用的请求级对象
session 每个 Session 新建 保存用户会话数据
application 整个 ServletContext 一个 全局共享数据
websocket 每个 WebSocket 会话新建 WebSocket 应用

4.5、总结

  • @Scope 用来控制 Bean 的生命周期。

  • 默认是 singleton 单例,如果需要多例、请求级或会话级的 Bean,就要用 @Scope 来指定。

5.@Configuration

5.1、是什么(What)

  • @Configuration 是 Spring 提供的一个 配置类注解
  • 标注在一个类上,表示这个类是 Spring 的配置类,相当于一个替代传统 XML 配置文件的 Java 类。
  • 在配置类里,可以用 @Bean 方法来声明 Bean。

👉 本质上,@Configuration 类本身也是一个 Spring 容器中的 Bean

👉 通过 CGLIB 动态代理 ,Spring 会拦截 @Bean 方法,确保无论调用多少次,返回的都是容器中同一个 Bean(单例)。


5.2、为什么(Why)

为什么要用 @Configuration

  1. 替代 XML 配置

    • 过去我们在 Spring 项目里要写:

      xml 复制代码
      <beans>
          <bean id="userService" class="com.example.UserService"/>
      </beans>
    • 有了 @Configuration 后,可以直接用 Java 代码声明:

      typescript 复制代码
      @Configuration
      public class AppConfig {
          @Bean
          public UserService userService() {
              return new UserService();
          }
      }
  2. 类型安全、可读性高

    • Java 配置比 XML 配置更直观,有编译器检查和 IDE 代码提示。
  3. 和 Spring Boot 无缝结合

    • Spring Boot 默认大量使用 @Configuration 类,配合 @EnableAutoConfiguration 进行自动装配。

5.3、怎么用(How)

5.3.1. 声明配置类
typescript 复制代码
@Configuration
public class AppConfig {
    
    @Bean
    public UserService userService() {
        return new UserService();
    }

    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }
}
5.3.2. 使用配置类
ini 复制代码
AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext(AppConfig.class);

UserService userService = context.getBean(UserService.class);
5.3.3. 单例保证(CGLIB 动态代理机制)
typescript 复制代码
@Configuration
public class MyConfig {
    @Bean
    public A a() {
        return new A(b()); // 注意这里调用了 b()
    }

    @Bean
    public B b() {
        return new B();
    }
}
  • 如果没有 @Configuration(比如只用了 @Component),每次调用 b() 都会 new 一个 B
  • 加上 @Configuration 后,Spring 用代理类接管了方法调用,b() 返回的始终是同一个 Bean(保证单例)。
5.3.4. 和 @Component 的区别
  • @Component:只是把类注册成一个 Bean。

  • @Configuration:是 专门用来写配置的类 ,里面的 @Bean 方法会被 Spring 扫描并加入容器。

  • 所以:

    • 如果你只是想让一个类成为 Bean → 用 @Component
    • 如果你要集中定义一批 Bean → 用 @Configuration

5.4、总结

注解 作用 是否代理 @Bean 方法 典型场景
@Component 把类注册成 Bean ❌ 不代理 普通组件类
@Configuration 声明配置类 ✅ 代理,保证单例 配置 Bean,替代 XML
  • @Configuration 用来声明配置类,里面的方法用 @Bean 定义 Bean。

  • 它比 @Component 更特殊,会通过 CGLIB 代理来保证 @Bean 方法返回的是同一个 Bean(单例)。

三.处理常见的 HTTP 请求类型

3.1、是什么(What)

HTTP 协议定义了多种 请求方法(Method) ,常用的有 5 种:

  1. GET:从服务器获取资源。
  2. POST:向服务器提交数据,创建新资源。
  3. PUT:更新资源(整体替换)。
  4. DELETE:删除资源。
  5. PATCH:更新资源的部分字段。

它们共同构成了 RESTful API 的核心操作


3.2、为什么(Why)

为什么需要区分不同请求类型?

  1. 语义清晰

    • 不同请求方法代表不同的操作语义,前后端一看就知道接口是做什么的。
    • 例如:GET /users → 获取用户;POST /users → 新增用户。
  2. 符合 RESTful 风格

    • RESTful API 倡导 资源 + 动作分离,动作用 HTTP 方法表示,资源用 URL 表示。
    • URL 只表示资源(如 /users/12),具体操作(获取/新增/更新/删除)由方法决定。
  3. 提升可维护性

    • 如果所有操作都用 POST,接口会混乱。
    • 采用不同方法,让接口风格一致,便于团队协作和自动化文档生成。

3.3、怎么用(How)

在 Spring Boot / Spring MVC 中,分别用不同的注解来映射请求方法:

3.3.1. GET 请求

  • 用途:获取数据(安全操作,不会修改服务器数据)。
  • Spring 注解@GetMapping@RequestMapping(method=GET)
kotlin 复制代码
@GetMapping("/users")
public List<User> getAllUsers() {
    return userRepository.findAll();
}

3.3.2. POST 请求

  • 用途:新增资源(非幂等,每次调用都会创建新资源)。
  • Spring 注解@PostMapping
less 复制代码
@PostMapping("/users")
public User createUser(@RequestBody User user) {
    return userRepository.save(user);
}

3.3.3. PUT 请求

  • 用途:整体更新资源(幂等,多次调用结果一样)。
  • Spring 注解@PutMapping
less 复制代码
@PutMapping("/users/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
    user.setId(id);
    return userRepository.save(user);
}

3.3.4. DELETE 请求

  • 用途:删除资源(幂等,多次删除同一个资源结果一样)。
  • Spring 注解@DeleteMapping
less 复制代码
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable Long id) {
    userRepository.deleteById(id);
}

3.3.5. PATCH 请求

  • 用途:部分更新资源(非幂等,通常用于更新部分字段)。
  • Spring 注解@PatchMapping
less 复制代码
@PatchMapping("/users/{id}")
public User updateUserPartially(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
    User user = userRepository.findById(id).orElseThrow();
    if (updates.containsKey("name")) {
        user.setName((String) updates.get("name"));
    }
    return userRepository.save(user);
}

3.4、总结表

方法 含义 幂等性 Spring 注解 示例
GET 获取资源 ✅ 幂等 @GetMapping GET /users
POST 新建资源 ❌ 非幂等 @PostMapping POST /users
PUT 整体更新 ✅ 幂等 @PutMapping PUT /users/12
DELETE 删除资源 ✅ 幂等 @DeleteMapping DELETE /users/12
PATCH 部分更新 ❌ 通常非幂等 @PatchMapping PATCH /users/12
markdown 复制代码
-   **是什么**:HTTP 的 5 种常见请求类型,代表获取/新增/更新/删除/部分更新资源。

-   **为什么**:语义清晰、符合 RESTful 风格、便于维护。

-   **怎么用**:在 Spring Boot 里用 `@GetMapping/@PostMapping/@PutMapping/@DeleteMapping/@PatchMapping` 来实现对应接口。

四.前后端传值

4.1、@PathVariable

4.1.1、是什么

  • @PathVariable 是 Spring MVC 提供的注解,用来 绑定 URL 路径中的变量 到方法参数。
  • 常用于 RESTful 风格的接口,例如 /users/{id}

4.1.2、为什么

  • 在 RESTful API 中,URL 常常包含资源标识符(如用户 ID)。
  • @PathVariable 可以方便地将 URL 中的变量提取出来,避免手动解析字符串,提高代码可读性。

4.1.3、怎么用

less 复制代码
@RestController
@RequestMapping("/users")
public class UserController {

    // GET /users/12
    @GetMapping("/{id}")
    public String getUserById(@PathVariable("id") Long userId) {
        return "用户ID: " + userId;
    }
}
  • 请求:GET /users/12
  • 输出:用户ID: 12

如果方法参数名和路径变量名一致,可以省略 ("id")

4.1.4、总结

  • @PathVariable 用于 URL 路径变量(资源定位)。

4.2、@RequestParam

4.2.1、是什么

  • @RequestParam 是 Spring MVC 提供的注解,用来 获取 URL 请求参数(? 后面的部分)或者表单参数。

4.2.2、为什么

  • 常见的 GET 请求和表单提交会传递查询参数(如分页 page、size),用 @RequestParam 能直接映射到方法参数,简化代码。

4.2.3、怎么用

less 复制代码
@RestController
@RequestMapping("/users")
public class UserController {

    // GET /users?page=1&size=10
    @GetMapping
    public String getUsers(@RequestParam("page") int page,
                           @RequestParam(value = "size", required = false, defaultValue = "20") int size) {
        return "分页参数: page=" + page + ", size=" + size;
    }
}
  • 请求:GET /users?page=1&size=10
  • 输出:分页参数: page=1, size=10
  • required = false:参数可选
  • defaultValue = "20":没有传递时使用默认值

4.2.4、总结

  • @RequestParam 用于 URL 查询参数表单参数(条件、筛选)。

4.3、@PathVariable@RequestParam的区别总结

特性 @PathVariable @RequestParam
绑定位置 URL 路径中的占位符 URL 的查询参数或表单参数
示例 URL /users/12 /users?page=1&size=10
典型用途 获取资源标识符 获取分页、搜索、筛选条件
是否可选 一般必须有(除非配置可选路径) 可设置 required=false 并提供 defaultValue

4.4、@RequestBody

4.4.1. 是什么

  • @RequestBody 是 Spring MVC 提供的注解,作用是 把 HTTP 请求体(Request Body)中的 JSON / XML / 表单数据,自动反序列化并绑定到方法参数对象
  • 底层依赖 HttpMessageConverter (常见是 Jackson 解析 JSON)。

4.4.2. 为什么

  • 在前后端分离的项目中,前端常用 POST/PUT 请求并发送 JSON 格式数据。
  • 如果不用 @RequestBody,需要手动读取请求体、再自己解析 JSON,非常麻烦。
  • 有了 @RequestBody,Spring 会自动帮你把 JSON 转换为 Java 对象,提高开发效率和可维护性。

4.4.3. 怎么用

(1)绑定到单个对象
less 复制代码
@RestController
@RequestMapping("/users")
public class UserController {

    // POST /users
    // 请求体: {"id":1, "name":"Tom", "age":20}
    @PostMapping
    public String createUser(@RequestBody User user) {
        return "创建用户: " + user.getName() + ", 年龄: " + user.getAge();
    }
}

请求示例:

bash 复制代码
POST /users
Content-Type: application/json

{
  "id": 1,
  "name": "Tom",
  "age": 20
}

返回:

makefile 复制代码
创建用户: Tom, 年龄: 20

(2)绑定到集合
less 复制代码
@PostMapping("/batch")
public String createUsers(@RequestBody List<User> users) {
    return "接收了 " + users.size() + " 个用户";
}

请求体:

css 复制代码
[  {"id":1,"name":"Tom","age":20},  {"id":2,"name":"Jerry","age":22}]

(3)和 @RequestParam / @PathVariable 混用
less 复制代码
// PUT /users/10?notify=true
@PutMapping("/{id}")
public String updateUser(@PathVariable Long id,
                         @RequestParam(defaultValue = "false") boolean notify,
                         @RequestBody User user) {
    return "更新用户ID=" + id + ", 新名字=" + user.getName() + ", 是否通知=" + notify;
}

4.4.4. 注意事项 ⚠️

  1. 必须有 Content-Type: application/json,否则可能报错。
  2. 需要依赖 Jackson 或其他 JSON 库(Spring Boot 默认集成)。
  3. @RequestBody 只能从 请求体 获取数据,不能获取 URL 参数。
  4. 如果是表单提交(application/x-www-form-urlencoded),推荐用 @RequestParam
  5. 一个请求方法只可以有一个@RequestBody,但是可以有多个@RequestParam@PathVariable

4.4.5. 总结

  • @RequestBody 用来把请求体中的 JSON 数据转成 Java 对象,特别适合前后端分离的场景。

五. 读取配置信息

5.1、是什么(What)

Spring 提供多种方式读取配置文件中的信息,常用的有三种:

  1. @Value:读取单个配置属性。
  2. @ConfigurationProperties :将配置文件中的属性绑定到一个 Java Bean 上,支持嵌套对象和集合。
  3. @PropertySource :指定读取某个 properties 文件(不常用,多用于老项目)。

配置文件可以是 application.ymlapplication.properties


5.2、为什么(Why)

  • 项目中会有很多 可变参数或外部配置,比如:

    • 数据库连接
    • 阿里云 OSS 配置
    • 微信小程序/公众号配置
    • 第三方 API Key
  • 将这些信息写死在代码中不好维护,也不安全。

  • Spring 提供的注解可以:

    • 自动注入配置值到 Bean
    • 支持类型安全
    • 支持集合和嵌套对象
    • 便于不同环境切换(如 application-dev.ymlapplication-prod.yml

5.3、怎么用(How)

5.3.1. 使用 @Value(最简单)

arduino 复制代码
# application.yml
wuhan2020: "2020年初武汉爆发了新型冠状病毒,疫情严重。"
kotlin 复制代码
@Component
public class WuhanInfo {

    @Value("${wuhan2020}")
    private String wuhan2020;

    public void printInfo() {
        System.out.println(wuhan2020);
    }
}
  • 适合读取 单个简单值
  • 支持默认值:
kotlin 复制代码
@Value("${wuhan2020:武汉加油}")
private String wuhan2020;

5.3.2. 使用 @ConfigurationProperties(常用,支持复杂对象)

yaml 复制代码
# application.yml
library:
  location: 湖北武汉
  books:
    - name: 天才基本法
      description: 林朝夕故事
    - name: 时间的秩序
      description: 时间本质探讨
typescript 复制代码
@Component
@ConfigurationProperties(prefix = "library")
public class LibraryProperties {
    private String location;
    private List<Book> books;

    @Data // lombok 提供 getter/setter/toString
    public static class Book {
        private String name;
        private String description;
    }
}
csharp 复制代码
@Component
public class LibraryService {

    private final LibraryProperties libraryProperties;

    public LibraryService(LibraryProperties libraryProperties) {
        this.libraryProperties = libraryProperties;
    }

    public void printLibraryInfo() {
        System.out.println(libraryProperties.getLocation());
        libraryProperties.getBooks().forEach(System.out::println);
    }
}
  • 适合读取 复杂对象和集合
  • 支持 类型安全、校验注解 (如 @NotEmpty)。

5.3.3. 使用 @PropertySource(不常用)

ini 复制代码
# website.properties
url=https://www.example.com
less 复制代码
@Component
@PropertySource("classpath:website.properties")
public class Website {
    @Value("${url}")
    private String url;

    public void printUrl() {
        System.out.println(url);
    }
}
  • 适合读取 非 Spring Boot 默认配置文件
  • 在 Spring Boot 项目中通常用不到,更多用于老的 Spring 项目。

5.4、总结表

注解 适合场景 支持复杂对象 示例配置文件 使用方式
@Value 单个属性 wuhan2020: "信息" @Value("${wuhan2020}")
@ConfigurationProperties 一组属性或嵌套对象 library.locationlibrary.books @ConfigurationProperties(prefix="library")
@PropertySource 指定 properties 文件 website.properties @PropertySource("classpath:website.properties")
markdown 复制代码
-   **简单值** → `@Value`

-   **复杂对象 / 集合** → `@ConfigurationProperties`

-   **指定文件** → `@PropertySource`

六. 参数校验

6.1、是什么(What)

  • 参数校验 是对前端传入的数据进行 合法性检查,防止非法或异常数据进入后端。
  • Spring Boot 使用 JSR 标准(Java Specification Requests)Hibernate Validator 来实现校验。
  • 常用注解在 javax.validation.constraints 包中,包括:
注解 作用
@Null 被注释的元素必须为 null
@NotNull 不能为 null
@NotEmpty 字符串或集合不能为空
@NotBlank 字符串不能为空且必须有非空白字符
@Email 必须是合法邮箱
@Size(min=, max=) 字符串或集合长度/大小限制
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Pattern(regex=,flag=) 必须符合指定正则
@Min/@Max 数值最小/最大值
@DecimalMin/@DecimalMax 小数最小/最大值
@Past/@Future 日期必须为过去/将来
@AssertTrue/@AssertFalse 布尔值必须为 true/false

6.2、为什么(Why)

  • 前端校验容易被绕过,用户可以直接用 HTTP 工具发送非法请求。

  • 后端统一校验可保证 数据完整性和安全性

  • Spring Boot 集成 Hibernate Validator,自动支持:

    • 校验请求体(@RequestBody
    • 校验请求参数(@PathVariable / @RequestParam
  • 提供统一的异常处理机制,可以返回标准错误信息给前端。


6.3、怎么用(How)

6.3.1. 验证请求体(@RequestBody

less 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    @NotNull(message = "classId 不能为空")
    private String classId;

    @Size(max = 33)
    @NotNull(message = "name 不能为空")
    private String name;

    @Pattern(regexp = "(^Man$|^Woman$|^UGM$)", message = "sex 值不在可选范围")
    @NotNull(message = "sex 不能为空")
    private String sex;

    @Email(message = "email 格式不正确")
    @NotNull(message = "email 不能为空")
    private String email;
}
less 复制代码
@RestController
@RequestMapping("/api")
public class PersonController {

    @PostMapping("/person")
    public ResponseEntity<Person> createPerson(@RequestBody @Valid Person person) {
        return ResponseEntity.ok(person);
    }
}
  • 使用 @Valid 注解触发校验。
  • 校验失败会抛出 MethodArgumentNotValidException

6.3.2. 验证请求参数(@PathVariable / @RequestParam

  • 一定要在类上加 @Validated,Spring 才会校验方法参数。
less 复制代码
@RestController
@RequestMapping("/api")
@Validated
public class PersonController {

    // GET /api/person/3
    @GetMapping("/person/{id}")
    public ResponseEntity<Integer> getPersonById(
            @PathVariable @Max(value = 5, message = "超过 id 的范围了") Integer id) {
        return ResponseEntity.ok(id);
    }
}
  • 可以在方法参数上使用 JSR 注解,如 @Max, @Min, @NotNull 等。
  • 校验失败会抛出 ConstraintViolationException

6.3.3. 异常统一处理(可选)

scss 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<String> handleValidException(MethodArgumentNotValidException ex) {
        String msg = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        return ResponseEntity.badRequest().body(msg);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<String> handleConstraintException(ConstraintViolationException ex) {
        String msg = ex.getConstraintViolations().iterator().next().getMessage();
        return ResponseEntity.badRequest().body(msg);
    }
}
  • 可以统一处理校验异常,返回友好的提示信息给前端。

6.4、总结表

校验方式 使用场景 注解 配合注解
请求体 @RequestBody JSON 对象 @NotNull, @Size, @Email... @Valid
方法参数 @PathVariable / @RequestParam @Max, @Min, @NotNull... @Validated
markdown 复制代码
-   **参数校验**保证后端数据安全和完整性;

-   **JSR 注解 + Spring Boot** 能轻松校验请求体和请求参数;

-   `@Valid` 用于对象,`@Validated` 用于方法参数。

七. 全局处理 Controller 层异常

7.1、是什么

在 Spring MVC 项目中,当 Controller 方法抛出异常时,如果没有统一处理,异常会直接冒泡到前端,返回的是复杂的堆栈信息(不友好,也不安全)。

Spring 提供了 全局异常处理机制

  • @ControllerAdvice:标记一个类为全局异常处理类,可以捕获所有 Controller 层的异常。
  • @ExceptionHandler:在 @ControllerAdvice 标注的类里,声明具体的异常处理方法。

7.2、为什么要用

主要解决几个问题:

  1. 统一管理异常:不用在每个 Controller 方法里写 try-catch。
  2. 返回统一格式 :比如所有异常返回 {code, message, data} 的 JSON。
  3. 增强可维护性:异常逻辑和业务逻辑解耦。
  4. 安全性:避免系统堆栈直接暴露给前端。

7.3、怎么用

7.3.1. 示例代码

less 复制代码
// 定义统一的响应结构
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
    private Integer code;  // 状态码
    private String message; // 提示信息
    private T data;        // 数据
}
typescript 复制代码
// 全局异常处理类
@ControllerAdvice
@ResponseBody   // 保证返回 JSON,而不是页面
public class GlobalExceptionHandler {

    /**
     * 处理参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        // 获取第一个错误提示
        String errorMsg = ex.getBindingResult()
                            .getFieldError()
                            .getDefaultMessage();
        return new Result<>(400, errorMsg, null);
    }

    /**
     * 处理空指针异常
     */
    @ExceptionHandler(NullPointerException.class)
    public Result<?> handleNullPointerException(NullPointerException ex) {
        return new Result<>(500, "系统发生空指针错误,请联系管理员!", null);
    }

    /**
     * 处理所有未捕获的异常
     */
    @ExceptionHandler(Exception.class)
    public Result<?> handleException(Exception ex) {
        return new Result<>(500, "系统异常:" + ex.getMessage(), null);
    }
}

7.3.2. 使用效果

  • 当 Controller 抛出异常(例如参数不合法、空指针等),会被 GlobalExceptionHandler 捕获。
  • 前端拿到的就是统一格式的 JSON:
json 复制代码
{
  "code": 400,
  "message": "用户名不能为空",
  "data": null
}

7.4、总结

  • 是什么 :Spring 提供的全局异常捕获机制(@ControllerAdvice + @ExceptionHandler)。

  • 为什么:统一管理异常、规范返回结构、提升安全性和可维护性。

  • 怎么用 :写一个带 @ControllerAdvice 的类,定义多个 @ExceptionHandler 方法处理不同异常。

八. JPA相关

8.1、创建表

8.1.1. 是什么

  • @Entity:声明一个类是 JPA 实体,映射到数据库表。
  • @Table:设置数据库表名、索引等。

8.1.2. 为什么

  • JPA 通过注解映射实体类与数据库表,开发者无需手写 SQL 建表语句,大大提高效率。

8.1.3. 怎么用

less 复制代码
@Entity
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增
    private Long id;

    private String name;
    private String description;
}

👉 数据库会自动生成 role 表。


8.2、创建主键

8.2.1. 是什么

  • @Id:声明主键字段。
  • @GeneratedValue:定义主键生成策略。

8.2.2. 为什么

  • 不同数据库支持的主键策略不同,需要灵活选择(自增、序列、UUID 等)。

8.2.3. 怎么用

常见 4 种策略:

arduino 复制代码
public enum GenerationType {
    TABLE,      // 通过一张表维护主键
    SEQUENCE,   // 使用数据库序列 (Oracle, PostgreSQL)
    IDENTITY,   // 主键自增 (MySQL 常用)
    AUTO        // JPA 自动选择 (默认)
}

示例:

less 复制代码
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

自定义主键策略:

less 复制代码
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;

8.2.4. jpa 提供的主键生成策略有如下几种:

scss 复制代码
/**
 * Hibernate 默认的主键生成器工厂
 * 实现了 MutableIdentifierGeneratorFactory,用于注册各种主键生成策略
 */
public class DefaultIdentifierGeneratorFactory
        implements MutableIdentifierGeneratorFactory, Serializable, ServiceRegistryAwareService {

    @SuppressWarnings("deprecation")
    public DefaultIdentifierGeneratorFactory() {
        // 注册 UUID v2 生成器(基于 Java UUID,推荐使用的 UUID 生成方式)
        register("uuid2", UUIDGenerator.class);

        // 注册 GUID 生成器(和 uuid2 类似,但依赖数据库/操作系统生成全局唯一标识)
        register("guid", GUIDGenerator.class);

        // 注册 UUID 十六进制生成器(旧版 UUID,已经不推荐使用)
        register("uuid", UUIDHexGenerator.class);

        // 注册 uuid.hex,等价于 uuid(已废弃)
        register("uuid.hex", UUIDHexGenerator.class);

        // 注册 Assigned 策略 ------ 由开发者手动赋值主键,而不是自动生成
        register("assigned", Assigned.class);

        // 注册 Identity 策略 ------ 使用数据库自增字段生成主键(MySQL 常用)
        register("identity", IdentityGenerator.class);

        // 注册 Select 策略 ------ 通过查询数据库获取主键(少见)
        register("select", SelectGenerator.class);

        // 注册 Sequence 策略 ------ 使用数据库序列生成主键(Oracle、PostgreSQL 常用)
        register("sequence", SequenceStyleGenerator.class);

        // 注册 seqhilo ------ 基于高低位(hi/lo)的序列生成器(性能优化用,较少使用)
        register("seqhilo", SequenceHiLoGenerator.class);

        // 注册 Increment 策略 ------ 简单自增(非线程安全,不推荐生产使用)
        register("increment", IncrementGenerator.class);

        // 注册 Foreign 策略 ------ 主键来自另一张表的外键(常见于一对一关联关系)
        register("foreign", ForeignGenerator.class);

        // 注册 sequence-identity ------ 序列 + 自增混合模式
        register("sequence-identity", SequenceIdentityGenerator.class);

        // 注册 enhanced-sequence ------ 增强版序列生成器(推荐替代旧的 sequence)
        register("enhanced-sequence", SequenceStyleGenerator.class);

        // 注册 enhanced-table ------ 增强版表生成器(通过维护一张表来生成主键)
        register("enhanced-table", TableGenerator.class);
    }

    /**
     * 注册主键生成策略
     * @param strategy 策略名称(如 "uuid"、"identity")
     * @param generatorClass 对应的生成器实现类
     */
    public void register(String strategy, Class generatorClass) {
        LOG.debugf("Registering IdentifierGenerator strategy [%s] -> [%s]",
                strategy, generatorClass.getName());

        final Class previous = generatorStrategyToClassNameMap.put(strategy, generatorClass);
        if (previous != null) {
            LOG.debugf("    - overriding [%s]", previous.getName());
        }
    }
}
  • 各种主键生成策略对照表
策略名 对应类 说明 适用场景
uuid2 UUIDGenerator 使用 Java UUID v2 生成 推荐,分布式环境
guid GUIDGenerator GUID,全局唯一标识 Windows/SQL Server
uuid / uuid.hex UUIDHexGenerator 旧版 UUID 已废弃
assigned Assigned 主键由应用代码手动赋值 业务系统已有主键
identity IdentityGenerator 数据库自增主键 MySQL 常用
sequence SequenceStyleGenerator 数据库序列 Oracle/PostgreSQL
seqhilo SequenceHiLoGenerator hi/lo 算法生成主键 大并发优化
increment IncrementGenerator 自增计数器 单机测试用,不推荐生产
foreign ForeignGenerator 使用外键作为主键 一对一关系
sequence-identity SequenceIdentityGenerator 序列 + 自增 特殊数据库
enhanced-sequence SequenceStyleGenerator 增强版序列 替代旧 sequence
enhanced-table TableGenerator 通过表维护主键 无序列支持的数据库

8.3、设置字段类型

8.3.1. 是什么

  • @Column:声明数据库列属性(名称、长度、非空、默认值等)。

8.3.2. 为什么

  • 可以精确控制数据库表字段的属性。

8.3.3. 怎么用

ini 复制代码
@Column(name = "user_name", nullable = false, length = 32)
private String userName;

@Column(columnDefinition = "tinyint(1) default 1")
private Boolean enabled;

8.4、指定不持久化的字段

8.4.1. 是什么

  • @Transient:该字段不会映射到数据库列。

8.4.2. 为什么

  • 某些字段只在业务逻辑中使用,不需要保存到数据库。

8.4.3. 怎么用

typescript 复制代码
@Transient
private String secrect;

8.5、声明大字段

8.5.1. 是什么

  • @Lob:声明大字段(如 TEXT, BLOB)。

8.5.2. 为什么

  • 存储大文本(文章内容)或二进制数据(图片、文件)。

8.5.3. 怎么用

less 复制代码
@Lob
@Basic(fetch = FetchType.EAGER)
@Column(name = "content", columnDefinition = "LONGTEXT NOT NULL")
private String content;

8.6、创建枚举字段

8.6.1. 是什么

  • @Enumerated:声明字段是枚举类型。

8.6.2. 为什么

  • 可以把枚举保存为 intString,可读性更好。

8.6.3. 怎么用

kotlin 复制代码
public enum Gender {
    MALE("男性"), FEMALE("女性");
    private String value;
    Gender(String str){ value = str; }
}

@Entity
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Enumerated(EnumType.STRING) // 存储为字符串
    private Gender gender;
}

数据库保存结果:MALEFEMALE


8.7、审计功能(自动维护创建时间/修改时间)

8.7.1. 是什么

  • @CreatedDate@LastModifiedDate:自动填充创建时间和修改时间。
  • @CreatedBy@LastModifiedBy:自动填充创建人和修改人。
  • @EnableJpaAuditing:开启 JPA 审计功能。

8.7.2. 为什么

  • 开发者无需手动维护时间戳和用户,避免出错。

8.7.3. 怎么用

抽象基类:

less 复制代码
@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AbstractAuditBase {

    @CreatedDate
    @Column(updatable = false)
    private Instant createdAt;

    @LastModifiedDate
    private Instant updatedAt;

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String updatedBy;
}

配置类:

less 复制代码
@Configuration
@EnableJpaAuditing
public class AuditSecurityConfiguration {
    @Bean
    AuditorAware<String> auditorAware() {
        return () -> Optional.ofNullable(
                SecurityContextHolder.getContext().getAuthentication().getName()
        );
    }
}

8.8、修改/删除数据

8.8.1. 是什么

  • @Modifying:标识修改/删除操作。
  • @Transactional:保证事务。

8.8.2. 为什么

  • 默认 JPA Repository 只支持简单 CRUD,自定义 SQL 需要额外标记。

8.8.3. 怎么用

less 复制代码
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    
    @Modifying
    @Transactional(rollbackFor = Exception.class)
    void deleteByUserName(String userName);

    @Modifying
    @Transactional
    @Query("update User u set u.name = ?1 where u.id = ?2")
    void updateUserNameById(String name, Long id);
}

8.9、关联关系

8.9.1 是什么

JPA 提供注解来描述实体之间的关系,常见的四种关系类型:

  • @OneToOne:一对一
  • @OneToMany:一对多
  • @ManyToOne:多对一
  • @ManyToMany:多对多

这些注解可以将面向对象中的对象关系映射到数据库表的外键、关联表上,实现对象关系映射(ORM)。


8.9.2 为什么

  • 在面向对象编程中,实体类之间存在引用关系(比如用户有一个详细资料,部门有多个员工)。
  • 在关系型数据库中,表之间通常用 外键或关联表 建立关系。
  • 使用 JPA 注解可以自动将对象关系映射到数据库关系,简化开发,并可支持 级联操作、懒加载、事务管理

8.9.3 怎么用

(1) 一对一 (@OneToOne)
  • 说明:一个实体对应另一个实体,数据库表中通常通过外键关联。

  • 常用属性:

    • mappedBy:被动方不拥有外键,用在关系的另一方。
    • cascade:级联操作,如 CascadeType.ALL 包括新增、更新、删除。
    • fetch:懒加载或立即加载,FetchType.LAZY / FetchType.EAGER
less 复制代码
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // 一个用户对应一个详细资料
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "profile_id") // 外键字段
    private UserProfile profile;
}
  • 级联删除 :删除 User 时,UserProfile 会被自动删除(因为 CascadeType.ALL 包含 REMOVE)。

(2) 一对多 + 多对一 (@OneToMany / @ManyToOne)
  • 说明:一个实体对应多个实体,多方实体对应一个实体。
  • mappedBy 用在一方,表示由多方维护外键。
less 复制代码
@Entity
public class Department {
    @Id
    private Long id;

    // 一个部门有多个员工
    @OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Employee> employees;
}

@Entity
public class Employee {
    @Id
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id") // 外键
    private Department department;
}
  • 说明

    • 删除 Department 时,如果 cascade = CascadeType.ALL + orphanRemoval = true,对应员工会自动删除。
    • FetchType.LAZY 可以延迟加载员工列表,避免一次性加载所有员工。

(3) 多对多 (@ManyToMany)
  • 说明:两个实体之间多对多关系,通常通过 中间关联表 维护。
less 复制代码
@Entity
public class Student {
    @Id
    private Long id;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
    @JoinTable(
        name = "student_course", // 中间表
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private List<Course> courses;
}

@Entity
public class Course {
    @Id
    private Long id;

    @ManyToMany(mappedBy = "courses") // 被动方
    private List<Student> students;
}
  • 说明

    • 中间表 student_course 保存学生和课程的对应关系。
    • 删除 StudentCourse 时,关联表的数据会同步删除,但默认不会删除对方实体。
    • cascadeorphanRemoval 可以控制级联操作。

8.9.4 其他常用属性和最佳实践

属性/注解 作用
cascade 设置级联操作类型,如 PERSIST、MERGE、REMOVE、ALL
orphanRemoval 是否删除孤儿对象(从集合中移除即删除数据库记录)
fetch 延迟加载 LAZY 或立即加载 EAGER
mappedBy 指定关系维护方,一方加 mappedBy 表示不维护外键
@JoinColumn 指定外键字段名
@JoinTable 多对多关系中指定中间表和关联列

8.10、总结

  • 表相关@Entity @Table @Column @Id

  • 主键策略IDENTITY / SEQUENCE / UUID

  • 字段控制@Transient @Lob @Enumerated

  • 审计功能@EnableJpaAuditing + @CreatedDate

  • 修改删除@Modifying @Transactional

  • 关联关系@OneToOne @OneToMany @ManyToOne @ManyToMany

8.11、JPA 常用注解速查表

分类 注解 说明
表相关 @Entity 标记一个类为 JPA 实体类,映射数据库表
@Table(name = "xxx") 指定实体类对应的表名(默认类名)
@Column(name = "xxx", nullable = false, length = 100) 指定字段名、长度、是否可空等约束
@Id 标识 主键字段
主键策略 @GeneratedValue(strategy = GenerationType.IDENTITY) 自增主键(MySQL 常用)
@GeneratedValue(strategy = GenerationType.SEQUENCE) 使用数据库序列生成主键(Oracle 常用)
@GeneratedValue(strategy = GenerationType.UUID) 使用 UUID 作为主键(Spring Boot 3.0+ 支持)
字段控制 @Transient 该字段 不持久化(不会映射到表)
@Lob 存储大字段(如 TEXTBLOB
@Enumerated(EnumType.STRING) 枚举保存为 字符串
@Enumerated(EnumType.ORDINAL) 枚举保存为 序号
审计功能 @EnableJpaAuditing 开启 JPA 审计功能(放在配置类上)
@CreatedDate 自动填充 创建时间
@LastModifiedDate 自动填充 修改时间
@CreatedBy 自动填充 创建人
@LastModifiedBy 自动填充 修改人
修改删除 @Modifying 标记 更新/删除 SQL 方法(配合 @Query
@Transactional 开启事务,保证修改/删除的原子性
关联关系 @OneToOne 一对一关系(如用户与身份证)
@OneToMany 一对多关系(如用户与订单)
@ManyToOne 多对一关系(如订单属于用户)
@ManyToMany 多对多关系(如学生与课程)

九. 事务@Transactional

9.1、是什么

@Transactional 是 Spring 提供的 声明式事务管理注解 ,用于定义方法或类的事务边界。

主要特点:

  • 可以自动开启、提交或回滚事务。
  • 支持配置事务的传播行为、隔离级别、回滚规则、超时时间等属性。
  • 可作用于方法或类。

9.2、为什么需要事务

在数据库操作中,经常存在 多步操作,例如:

  1. 保存用户信息。
  2. 保存用户权限。
  3. 保存操作日志。

如果中间某步失败而前面已执行成功,会造成 数据不一致

事务能保证:

  • 原子性:所有操作要么全部成功,要么全部回滚。
  • 一致性:事务执行前后数据保持一致。
  • 隔离性:并发事务之间互不干扰。
  • 持久性:事务提交后,数据永久存储。

总结:事务是保证数据库数据可靠性和一致性的核心机制。


9.3、怎么用

(1) 方法级事务

在单个方法上声明事务:

java 复制代码
@Service
public class UserService {

    @Transactional(rollbackFor = Exception.class) // 指定遇到 Exception 也回滚
    public void saveUser(User user) throws Exception {
        userRepository.save(user); // 保存用户
        // 模拟异常
        if(user.getName() == null) {
            throw new Exception("用户名不能为空"); // 会回滚
        }
        logRepository.save(new Log("新增用户")); // 保存日志
    }
}
  • 说明

    • rollbackFor: 指定哪些异常触发回滚,默认只有 RuntimeException 回滚。

    • noRollbackFor: 指定某些异常不回滚。

    • propagation: 事务传播行为,常用值:

      • REQUIRED(默认):支持当前事务,如果没有则新建。
      • REQUIRES_NEW:总是新建事务,挂起当前事务。
      • NESTED:嵌套事务。
    • isolation: 事务隔离级别,常用值:

      • READ_UNCOMMITTED:读未提交
      • READ_COMMITTED:读已提交
      • REPEATABLE_READ:可重复读(MySQL 默认)
      • SERIALIZABLE:串行化
    • timeout: 超时时间(秒),事务执行超过时间会回滚。


(2) 类级事务

在类上声明事务,类中所有 public 方法都会自动开启事务:

typescript 复制代码
@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {

    public void saveUser(User user) {
        userRepository.save(user);
    }

    public void updateUser(User user) {
        userRepository.save(user);
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}
  • 说明

    • 方法级 @Transactional 会覆盖类级的事务配置。
    • 对类中非 public 方法、private 方法不起作用。

(3) 注意事项

  1. 事务只对 public 方法有效

    • 因为 Spring 事务是通过 AOP 代理实现的,private/protected 方法不会被代理拦截。
  2. 内部方法调用导致事务失效

    • 同一个类内部调用方法(this.method()),事务不会生效,因为是绕过了代理对象。
    • 解决方案:通过 自调用注入自身 Bean 或将方法拆到不同类。
  3. 回滚策略

    • 默认:只有 RuntimeExceptionError 回滚。
    • 自定义:rollbackFor = Exception.class 让非运行时异常也回滚。
    • noRollbackFor = CustomException.class 让指定异常不回滚。
  4. 事务传播行为示例

typescript 复制代码
@Transactional
public void outer() {
    inner(); // 默认传播行为 REQUIRED,内层方法会加入外层事务
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void inner() {
    // 无论外层事务如何,inner 方法总是新建事务
}
  1. 事务与 JPA 关联

    • JPA 的增删改查操作,如果没有事务,可能 立即执行 SQL 或在 flush 时才执行。
    • @OneToMany@ManyToOne 等级联操作,事务必须存在,否则会报错或数据不一致。

9.4、总结

  • @Transactional 是 Spring 声明式事务的核心。
  • 支持方法级或类级配置。
  • 默认只回滚运行时异常,可通过 rollbackFor 配置回滚规则。
  • 事务传播、隔离、超时等配置可根据业务需求灵活调整。
  • 注意内部调用和 private 方法可能导致事务失效。

十. JSON 数据处理

10.1、过滤 JSON 数据

10.1.1. 是什么

过滤 JSON 数据就是在 序列化(Java 对象 → JSON)或反序列化(JSON → Java 对象) 时,排除掉某些字段不参与生成或解析。

Jackson 提供两个主要注解:

  1. @JsonIgnoreProperties:类级别,忽略指定字段。
  2. @JsonIgnore:字段级别,忽略该字段。

10.1.2. 为什么

  1. 保护敏感信息:如密码、权限列表不想暴露给前端。
  2. 减少数据冗余:有些字段对前端无用,省流量。
  3. 避免循环引用:对象之间相互引用时,过滤字段可防止 JSON 序列化报错。

10.1.3. 怎么用

1️⃣ @JsonIgnoreProperties(类级别)

typescript 复制代码
// 忽略 userRoles 字段
@JsonIgnoreProperties({"userRoles"})
public class User {
    private String userName;
    private String fullName;
    private String password;
    private List<UserRole> userRoles = new ArrayList<>();
}
  • 放在类上,数组中写要忽略的字段名。
  • 同时适用于序列化和反序列化。

2️⃣ @JsonIgnore(字段级别)

typescript 复制代码
public class User {
    private String userName;
    private String fullName;
    private String password;

    @JsonIgnore
    private List<UserRole> userRoles = new ArrayList<>();
}
  • 放在字段上,只有该字段被忽略。
  • 更精细,适合单个字段控制。

💡 区别@JsonIgnoreProperties 可以一次性忽略多个字段,@JsonIgnore 针对单个字段。


10.2、格式化 JSON 数据

10.2.1. 是什么

JSON 格式化指的是在序列化或反序列化时,对字段值进行特定格式化,比如日期格式。

Jackson 提供 @JsonFormat 注解。


10.2.2. 为什么

  1. 保证前后端统一:前端接收到的日期格式固定,避免解析错误。
  2. 满足业务需求 :有些接口需要特定时间格式,如 yyyy-MM-dd 或 ISO8601。

10.2.3. 怎么用

ini 复制代码
public class Event {
    @JsonFormat(
        shape = JsonFormat.Shape.STRING,
        pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
        timezone = "GMT"
    )
    private Date eventTime;
}
  • shape:数据形态,STRING 表示作为字符串输出。
  • pattern:日期格式。
  • timezone:时区。

序列化效果:

json 复制代码
{
  "eventTime": "2025-09-01T10:00:00.000Z"
}

10.3、扁平化对象

10.3.1. 是什么

扁平化对象是将 嵌套的对象字段"拉平" 到 JSON 的顶层,而不是保留嵌套结构。

使用注解:@JsonUnwrapped


10.3.2. 为什么

  1. 简化前端处理:前端不必访问嵌套对象字段。
  2. 接口风格统一:REST API 输出可以统一扁平化字段。
  3. 减少冗余嵌套:JSON 更清晰,数据结构更直观。

10.3.3. 怎么用

less 复制代码
@Getter
@Setter
@ToString
public class Account {
    @JsonUnwrapped
    private Location location;
    @JsonUnwrapped
    private PersonInfo personInfo;

    @Getter
    @Setter
    @ToString
    public static class Location {
        private String provinceName;
        private String countyName;
    }

    @Getter
    @Setter
    @ToString
    public static class PersonInfo {
        private String userName;
        private String fullName;
    }
}

未扁平化 JSON

json 复制代码
{
  "location": {
    "provinceName":"湖北",
    "countyName":"武汉"
  },
  "personInfo": {
    "userName": "coder1234",
    "fullName": "shaungkou"
  }
}

扁平化 JSON(使用 @JsonUnwrapped)

json 复制代码
{
  "provinceName":"湖北",
  "countyName":"武汉",
  "userName": "coder1234",
  "fullName": "shaungkou"
}

10.4、总结表格

功能 注解 作用 使用场景
过滤字段 @JsonIgnore / @JsonIgnoreProperties 序列化或反序列化时忽略字段 隐私字段、减少冗余、避免循环引用
格式化数据 @JsonFormat 控制字段的输出格式(如日期) 日期/时间格式统一,前后端一致
扁平化对象 @JsonUnwrapped 将嵌套对象字段拉平到顶层 接口简化、前端易用、JSON 美化

十一. 测试相关注解

11.1、@ActiveProfiles

11.1.1. 是什么

  • Spring Boot 提供的注解,用于指定 测试类运行时生效的配置文件(Spring Profile)。
  • 可以理解为告诉 Spring "在运行这个测试时,使用哪个环境的配置"。

11.1.2. 为什么

  1. 测试与开发环境配置不同,比如数据库、缓存等连接信息。
  2. 避免测试影响正式配置,保证测试隔离。
  3. 可以针对不同环境运行不同的测试数据或逻辑。

11.1.3. 怎么用

less 复制代码
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")  // 使用 application-test.yml 或 application-test.properties
@Slf4j
public abstract class TestBase {
    // 公共测试配置
}
  • "test" 对应 application-test.ymlapplication-test.properties
  • 支持多 profile:@ActiveProfiles({"test", "dev"})

11.2、@Test

11.2.1. 是什么

  • JUnit 提供的注解,用于声明一个方法为 测试方法
  • 测试框架会自动识别并运行标注了 @Test 的方法。

11.2.2. 为什么

  • 明确标记测试入口,框架自动执行。
  • 让测试方法与普通方法区分开,便于测试管理。

11.2.3. 怎么用

typescript 复制代码
@Test
void should_import_student_success() {
    // 测试逻辑
}
  • 方法可以抛异常,也可以包含断言(Assertions.assertEquals 等)。

11.3、@Transactional(测试方法)

11.3.1. 是什么

  • Spring 提供的事务管理注解,声明在测试方法上时,方法执行完毕后会回滚事务

11.3.2. 为什么

  1. 避免污染测试数据:测试中插入、修改的数据库记录不会影响正式数据。
  2. 保证每次测试独立:每次运行方法都是干净的数据状态。

11.3.3. 怎么用

less 复制代码
@Test
@Transactional
void should_save_user_success() {
    // 测试数据库操作,执行完自动回滚
}

💡 注意

  • 默认只在使用 Spring 测试上下文时生效(@SpringBootTest)。
  • 回滚可以通过 @Rollback(false) 禁用。

11.4、@WithMockUser(Spring Security)

11.4.1. 是什么

  • Spring Security 提供的测试注解,用于 模拟一个登录用户,并可赋予角色或权限。

11.4.2. 为什么

  1. 测试安全控制逻辑,比如接口权限校验。
  2. 避免每次测试都实际登录,方便自动化测试。

11.4.3. 怎么用

less 复制代码
@Test
@Transactional
@WithMockUser(username = "user-id-18163138155", authorities = "ROLE_TEACHER")
void should_import_student_success() throws Exception {
    // 模拟用户 ROLE_TEACHER 执行操作
}
  • username:模拟登录用户名
  • authorities:模拟用户角色或权限
  • 可组合使用 @Test@Transactional

11.5、总结表格

注解 作用 使用场景
@ActiveProfiles 指定测试使用的 Spring 配置文件 测试隔离环境,加载特定配置
@Test 声明测试方法 所有单元测试/集成测试
@Transactional 测试方法事务执行后回滚 测试数据库操作,保证数据不污染
@WithMockUser 模拟登录用户及角色权限 测试安全接口、权限校验
相关推荐
孤狼程序员10 小时前
【Spring Cloud微服务】7.拆解分布式事务与CAP理论:从理论到实践,打造数据一致性堡垒
spring·spring cloud·微服务
趙卋傑10 小时前
Spring原理
java·后端·spring
新鲜萝卜皮12 小时前
Spring如何解决循环依赖
spring
Jacobshash13 小时前
SpringCloud框架组件梳理
后端·spring·spring cloud
孤狼程序员13 小时前
深入探讨Java异常处理:受检异常与非受检异常的最佳实践
java·后端·spring
3Cloudream1 天前
互联网大厂Java面试:从基础到微服务的深度解析
java·spring·微服务·面试·技术解析·电商场景
C++chaofan1 天前
Spring Task快速上手
java·jvm·数据库·spring boot·后端·spring·mybatis
yinke小琪1 天前
Spring生态全家桶:从基础到微服务的演进与关联是什么?
java·后端·spring
一枚小小程序员哈1 天前
基于微信小程序的诊所信息系统的设计与实现
spring boot·struts·spring·spring cloud·java-ee·maven·intellij-idea