Spring Boot 核心注解完全手册

Spring Boot 核心注解完全手册(逻辑版·痛点+原理+实战)

本文按企业级开发流程逻辑重构,覆盖11大核心场景,每个注解均包含「核心功能+底层原理+可运行示例+高频痛点&解决方案」,既是开发手册也是排错指南(基于Spring Boot 3.2+)。

简洁版(后附详细版带原理解释)

Spring Boot 核心注解手册

按开发逻辑排序(启动→Bean→注入→配置→Web→全局→数据→高级→条件→安全→测试)

1. 启动与自动配置(应用入口)

注解 核心功能 关键/痛点
@SpringBootApplication 启动类核心,三合一(@SpringBootConfiguration+@EnableAutoConfiguration+@ComponentScan) 跨包Bean扫不到:用scanBasePackages指定路径
@EnableAutoConfiguration 开启自动配置,按需加载组件 看生效/失效:application.yml加debug: true
@Import 手动导入配置类/Bean,突破扫描限制 导入类无默认构造器:手动@Bean创建
@ComponentScan 显式指定Bean扫描路径 报错No qualifying bean:检查包路径/注解

2. IoC容器与Bean定义(组件注册)

注解 核心功能 关键/痛点
@Configuration 标记配置类,替代XML proxyBeanMethods=true:保证单例;false:启动更快
@Bean 方法返回值注册为Bean 原型Bean注入单例:用ObjectFactory延迟获取
@Component 通用Bean注解 多实例冲突:@Qualifier指定名称
@Service 业务层Bean(语义化@Component) 严格分层,别标在Controller上
@Repository DAO层Bean,自带持久层异常转换 MyBatis的@Mapper无需再加
@Controller MVC控制器,返回视图 返回JSON需加@ResponseBody
@RestController @Controller+@ResponseBody,默认返回JSON 字段名不一致:用@JsonProperty

3. 依赖注入与Bean控制(生命周期)

注解 核心功能 关键/痛点
@Autowired 按类型注入Bean 多Bean冲突:@Qualifier/@Primary;构造器注入最优
@Qualifier 配合@Autowired按名称注入 名称区分大小写,检查Bean名是否匹配
@Primary 同类型多Bean时首选 同一类型只能一个@Primary
@Resource JSR标准,默认按名称注入 Spring Boot3+用jakarta.annotation.Resource
@Scope 定义Bean作用域 原型Bean注入单例:用ObjectFactory/@Lookup
@Lazy 单例Bean懒加载 循环依赖:加@Lazy延迟加载
@PostConstruct Bean初始化后执行 构造器中无法用@Autowired,放这里
@PreDestroy Bean销毁前释放资源 仅单例生效,容器正常关闭才执行

4. 配置绑定与多环境(外部配置)

注解 核心功能 关键/痛点
@Value 注入单个配置值,支持${}/SpEL 静态变量注入:用非静态setter赋值
@ConfigurationProperties 批量绑定配置到Bean,支持List/Map/校验 绑定失败:检查prefix/setter/@EnableConfigurationProperties
@PropertySource 加载自定义.properties文件 不支持YAML:自定义PropertySourceFactory
@Profile 指定环境生效(dev/test/prod) 激活:spring.profiles.active=dev
@Validated 开启参数校验 需引入spring-boot-starter-validation依赖

5. Web MVC接口开发(HTTP请求)

注解 核心功能 关键/痛点
@RequestMapping/@GetMapping等 绑定请求路径/方法 PUT/DELETE被拦截:配置跨域/Security
@PathVariable 提取路径参数(/user/{id}) 类型转换失败:用正则限制({id:\d+})
@RequestParam 提取URL/表单参数 多值参数:前端传ids=1&ids=2
@RequestBody 接收JSON请求体 Content-Type必须是application/json
@RequestHeader/@CookieValue 提取请求头/Cookie 非必填:设置required=false
@CrossOrigin 允许跨域 跨域报错:检查origins/maxAge配置

6. 全局统一处理(AOP/异常)

注解 核心功能 关键/痛点
@RestControllerAdvice 全局异常处理,返回JSON 不生效:检查扫描路径/异常类型匹配
@ExceptionHandler 处理指定异常 优先级:Controller内>全局
@Aspect/@Pointcut/@Around等 AOP切面编程 内部调用不生效:走代理对象

7. 数据访问与事务(数据库)

注解 核心功能 关键/痛点
@Transactional 声明式事务 失效场景:非public/内部调用/异常被捕获/MyISAM引擎
@Entity/@Table/@Id等 JPA实体映射 主键策略:MySQL用IDENTITY,Oracle用SEQUENCE
@Mapper/@MapperScan MyBatis映射接口 多参数绑定:用@Param指定名称

8. 高级能力(异步/定时/消息)

注解 核心功能 关键/痛点
@EnableAsync/@Async 开启/标记异步方法 失效:未加@EnableAsync/内部调用;自定义线程池防耗尽
@EnableScheduling/@Scheduled 开启/标记定时任务 阻塞:自定义ThreadPoolTaskScheduler
@RabbitListener/@KafkaListener 消息队列消费者 需配置对应MQ连接信息

9. 条件装配(自动配置灵魂)

注解 核心功能 关键/痛点
@ConditionalOnClass/@ConditionalOnBean等 满足条件才加载Bean 不生效:debug模式看条件匹配结果

10. 安全与权限

注解 核心功能 关键/痛点
@EnableWebSecurity 开启Web安全 需配置SecurityFilterChain
@PreAuthorize 方法前权限校验(支持EL) 需开启prePostEnabled=true
@AuthenticationPrincipal 获取当前登录用户 需登录后才能获取,匿名访问为null

11. 测试与切片

注解 核心功能 关键/痛点
@SpringBootTest 启动完整容器,集成测试 启动慢:单元测试用切片注解
@WebMvcTest 只启动Web层,测试Controller 依赖Service需用@MockBean模拟
@DataJpaTest 只启动数据层,测试Repository 默认用H2,真实库需加Replace.NONE
@MockBean 模拟Bean,隔离依赖 区别@Mock:@MockBean放入Spring容器

核心逻辑总结(背会即通)

  1. 先启动:@SpringBootApplication;2. 装Bean:@Service/@Bean;3. 注入:@Autowired;4. 读配置:@ConfigurationProperties;5. 写接口:@RestController;6. 全局处理:@RestControllerAdvice;7. 数据库:@Transactional;8. 高级:@Async;9. 条件:@ConditionalOnXxx;10. 安全:@PreAuthorize;11. 测试:@WebMvcTest。

详细版

一、启动与自动配置(应用入口级)

1. @SpringBootApplication

核心功能

Spring Boot应用唯一入口注解,三合一组合注解,标记主启动类,触发自动配置和组件扫描。

底层原理
  • 包含3个核心注解:
    • @SpringBootConfiguration:本质是@Configuration,标记主类为配置类;
    • @EnableAutoConfiguration:触发自动配置机制;
    • @ComponentScan:默认扫描主类所在包及其子包的组件。
  • 启动时通过SpringApplication加载上下文,解析该注解完成初始化。
实际示例
java 复制代码
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// 核心入口:默认扫描com.example.demo及子包
@SpringBootApplication
// 自定义扫描路径(解决跨包扫描问题)
// @SpringBootApplication(scanBasePackages = {"com.example.service", "com.example.mapper"})
// 排除指定自动配置(解决冲突)
// @SpringBootApplication(exclude = RedisAutoConfiguration.class)
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
使用痛点&解决方案
痛点 解决方案
跨包的Bean扫描不到(如com.example.api下的Controller) 1. 用scanBasePackages指定所有需要扫描的包;2. 将主类包名提升为根包(如com.example)
自动配置冲突(如自定义RedisTemplate覆盖默认) exclude排除冲突的自动配置类;或用@ConditionalOnMissingBean自定义Bean覆盖
启动慢 缩小扫描范围,排除无需自动配置的组件

2. @EnableAutoConfiguration

核心功能

单独开启Spring Boot自动配置机制(通常随@SpringBootApplication生效),根据项目依赖自动配置组件。

底层原理
  1. 注解内通过@Import(AutoConfigurationImportSelector.class)导入自动配置选择器;
  2. AutoConfigurationImportSelector扫描META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件;
  3. 加载文件中所有XxxAutoConfiguration类,结合@ConditionalOnXxx条件判断是否生效。
实际示例
java 复制代码
// 极少单独使用,通常随@SpringBootApplication生效
@EnableAutoConfiguration
@Configuration
public class AutoConfigDemo {
    // 自动配置会根据依赖加载Tomcat、MVC、数据源等组件
}
使用痛点&解决方案
痛点 解决方案
不知道哪些自动配置生效/失效 在application.yml中添加debug: true,启动日志会打印Positive matches(生效)和Negative matches(失效)
自动配置类加载过多导致启动慢 @SpringBootApplication(exclude)排除无需的自动配置;或自定义starter缩小依赖范围

3. @Import

核心功能

手动导入配置类/普通类到Spring容器,突破@ComponentScan的扫描限制。

底层原理
  • 通过ImportBeanDefinitionRegistrarImportSelector将指定类注册为Bean;
  • 优先级高于@ComponentScan,可导入非扫描路径下的类。
实际示例
java 复制代码
package com.example.demo.config;

import com.example.external.ExternalService;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Configuration;

// 导入单个类
@Import(ExternalService.class)
// 导入多个类
// @Import({ExternalService.class, CustomConfig.class})
@Configuration
public class ImportConfig {
    // ExternalService会被注册为Bean,即使它不在扫描路径下
}
使用痛点&解决方案
痛点 解决方案
导入的类无默认构造器导致初始化失败 确保导入类有无参构造器;或通过@Bean手动创建实例
导入过多类导致配置混乱 按功能分组导入,避免一次性导入大量类

4. @ComponentScan

核心功能

显式指定Spring组件扫描路径,替代默认扫描规则。

底层原理
  • 通过ClassPathScanningCandidateComponentProvider扫描指定路径;
  • 识别标注@Component/@Service/@Controller等注解的类,注册为Bean。
实际示例
java 复制代码
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;

// 显式指定扫描路径
@ComponentScan(basePackages = {
    "com.example.demo.controller",
    "com.example.demo.service",
    "com.example.external" // 跨模块扫描
})
@org.springframework.boot.autoconfigure.SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
使用痛点&解决方案
痛点 解决方案
报错No qualifying bean of type 'XxxService' available 1. 检查XxxService是否加@Service;2. 检查basePackages是否包含该类所在包;3. 用basePackageClasses精准扫描(如basePackageClasses = UserService.class
扫描路径过大导致启动慢 缩小扫描范围,只扫描核心业务包,排除测试/工具包

二、IoC 容器与 Bean 定义(组件注册级)

1. @Configuration

核心功能

标记类为配置类,替代传统XML配置文件,类中@Bean方法的返回值会注册为Spring Bean。

底层原理
  • 默认通过CGLIB代理(proxyBeanMethods = true),保证@Bean方法多次调用返回单例;
  • proxyBeanMethods = false(Lite模式):无代理,启动更快,适合无@Bean方法互相调用的场景。
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

// 全量模式(默认):保证@Bean方法单例
@Configuration
// 轻量模式:启动更快,适合无@Bean方法依赖
// @Configuration(proxyBeanMethods = false)
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    // 全量模式下,多次调用返回同一个实例
    @Bean
    public String appName() {
        return "demo-app";
    }
}
使用痛点&解决方案
痛点 解决方案
配置类中调用@Bean方法返回多实例 保持proxyBeanMethods = true;或通过@Autowired注入Bean而非直接调用方法
配置类过多导致维护混乱 按功能拆分配置类(如RedisConfig、DataSourceConfig),用@Import批量导入

2. @Bean

核心功能

标注在@Configuration类的方法上,将方法返回值注册为Spring Bean,是手动注册Bean的核心方式。

底层原理
  • Spring解析@Configuration类时,将@Bean方法封装为BeanDefinition
  • 单例Bean:容器启动时执行方法创建实例;原型Bean:每次getBean()时执行方法。
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {

    // 基础用法:默认Bean名称=方法名
    @Bean
    public UserService userService() {
        return new UserService();
    }

    // 自定义Bean名称+初始化/销毁方法
    @Bean(
        name = "customDataSource",
        initMethod = "init",
        destroyMethod = "close"
    )
    public DataSource dataSource() {
        return new CustomDataSource();
    }

    // 依赖其他Bean(方法参数自动注入)
    @Bean
    public OrderService orderService(UserService userService) {
        return new OrderService(userService);
    }

    // 静态内部类模拟
    public static class CustomDataSource {
        public void init() { System.out.println("数据源初始化"); }
        public void close() { System.out.println("数据源销毁"); }
    }
}
使用痛点&解决方案
痛点 解决方案
@Bean方法依赖的Bean不存在 1. 检查依赖Bean是否注册;2. 用@ConditionalOnBean确保依赖存在;3. 捕获初始化异常
原型Bean注入到单例Bean中仅创建一次 ObjectFactory<XxxBean>延迟获取;或手动调用ApplicationContext.getBean()

3. @Component

核心功能

最通用的组件注解,标记类为Spring Bean,是所有业务组件注解的父注解。

底层原理
  • @Indexed修饰(Spring 5+),编译时生成META-INF/spring.components文件,提升扫描效率;
  • Spring扫描到该注解后,自动将类注册为Bean,默认名称为类名首字母小写。
实际示例
java 复制代码
package com.example.demo.component;

import org.springframework.stereotype.Component;

// 默认Bean名称:userComponent
@Component
// 自定义Bean名称
// @Component("myUserComponent")
public class UserComponent {

    public String getUserName(Long id) {
        return "用户" + id;
    }
}
使用痛点&解决方案
痛点 解决方案
同一类型多个@Component Bean导致注入冲突 1. 用@Qualifier指定Bean名称;2. 用@Primary标记首选Bean;3. 自定义Bean名称避免重复
工具类加@Component导致多实例 工具类建议设计为静态方法,无需注册为Bean;若必须注册,确保@Scope("singleton")(默认)

4. @Service

核心功能

标注业务逻辑层(Service)类,是@Component的语义化特例,无额外功能,但明确代码分层。

底层原理
  • 底层通过@Component实现(@Service = @Component + 业务层语义);
  • Spring扫描时与@Component无区别,仅作为代码分层标识。
实际示例
java 复制代码
package com.example.demo.service;

import org.springframework.stereotype.Service;

// 默认Bean名称:userService
@Service
// 自定义Bean名称
// @Service("userBiz")
public class UserService {

    public String getUserInfo(Long id) {
        // 业务逻辑:查询用户信息
        return "用户ID:" + id + ",名称:张三";
    }
}
使用痛点&解决方案
痛点 解决方案
误将@Service标注在Controller/Repository上 严格按分层标注:Controller→@Controller,Repository→@Repository,Service→@Service
Service层依赖注入失败 1. 检查是否加@Service;2. 检查@ComponentScan是否包含该包;3. 检查依赖的Bean是否存在

5. @Repository

核心功能

标注数据访问层(DAO/Repository)类,除注册Bean外,自动将持久层异常(如SQLException)转换为Spring统一的DataAccessException

底层原理
  • 继承@Component,具备Bean注册功能;
  • Spring通过PersistenceExceptionTranslationPostProcessor为该注解标注的类生成代理,实现异常转换。
实际示例
java 复制代码
package com.example.demo.repository;

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {

    public String findById(Long id) {
        // 模拟JDBC查询,抛出SQLException
        // throw new SQLException("查询失败");
        return "用户ID:" + id;
    }
}
使用痛点&解决方案
痛点 解决方案
异常转换功能失效 1. 确保类加@Repository;2. 确保Spring上下文存在PersistenceExceptionTranslationPostProcessor(Spring Boot自动配置);3. 异常必须是持久层异常(如SQLException)
与MyBatis的@Mapper混用 MyBatis的@Mapper已包含Bean注册和代理生成,无需再加@Repository

6. @Controller

核心功能

标注Spring MVC控制器类,处理HTTP请求,默认返回视图页面(如Thymeleaf/JSP)。

底层原理
  • 继承@Component,被Spring扫描为Bean;
  • Spring MVC通过RequestMappingHandlerMapping识别类中的@RequestMapping方法,绑定请求。
实际示例
java 复制代码
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class PageController {

    // 返回视图(需配置视图解析器,如Thymeleaf)
    @GetMapping("/user/page")
    public String userPage() {
        // 解析为:classpath:/templates/user.html
        return "user";
    }

    // 配合@ResponseBody返回JSON
    @GetMapping("/user/info")
    @ResponseBody
    public String userInfo() {
        return "{\"id\":1,\"name\":\"张三\"}";
    }
}
使用痛点&解决方案
痛点 解决方案
方法返回字符串被解析为视图路径而非JSON 1. 方法加@ResponseBody;2. 类上加@RestController替代@Controller
报错No mapping for GET /xxx 1. 检查@GetMapping路径是否正确;2. 检查Controller是否加@Controller;3. 检查扫描路径

7. @RestController

核心功能

组合注解(@Controller + @ResponseBody),标注控制器类,所有方法默认返回JSON/XML数据,无需重复加@ResponseBody

底层原理
  • 底层通过@Controller@ResponseBody元注解实现;
  • @ResponseBody通过HttpMessageConverter将返回值转换为JSON,写入HTTP响应体。
实际示例
java 复制代码
package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RestApiController {

    // 自动返回JSON,无需@ResponseBody
    @GetMapping("/order/info")
    public OrderVO getOrderInfo() {
        OrderVO vo = new OrderVO();
        vo.setId(1L);
        vo.setOrderNo("20240310001");
        vo.setAmount(99.9);
        return vo; // Spring自动转换为JSON
    }

    // 静态内部类:订单VO
    public static class OrderVO {
        private Long id;
        private String orderNo;
        private Double amount;

        // getter/setter省略
    }
}
使用痛点&解决方案
痛点 解决方案
返回的JSON字段名与实体类不一致 1. 用@JsonProperty("order_no")标注实体类字段;2. 配置Jackson自定义命名策略
返回值为null时字段丢失 在application.yml中配置:spring.jackson.default-property-inclusion=ALWAYS

三、依赖注入与 Bean 控制(生命周期/作用域)

1. @Autowired

核心功能

按类型(byType)自动装配Spring Bean,是依赖注入(DI)的核心注解,支持构造器、字段、方法注入。

底层原理
  1. Spring通过AutowiredAnnotationBeanPostProcessor解析该注解;
  2. 装配流程:按类型查找Bean → 找到多个则按名称匹配 → 匹配失败抛出异常;
  3. 优先级:构造器注入 > 方法注入 > 字段注入。
实际示例
java 复制代码
package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    // 字段注入(简单但不推荐,不利于单元测试)
    @Autowired
    private UserService userService;

    // 构造器注入(推荐,强制依赖,便于单元测试)
    private final ProductService productService;

    @Autowired
    public OrderService(ProductService productService) {
        this.productService = productService;
    }

    // 方法注入(适合可选依赖)
    private PayService payService;

    @Autowired(required = false)
    public void setPayService(PayService payService) {
        this.payService = payService;
    }

    // 多Bean时配合@Qualifier
    @Autowired
    @Qualifier("vipUserService")
    private UserService vipUserService;
}
使用痛点&解决方案
痛点 解决方案
报错No qualifying bean of type 'XxxService' available 1. 检查XxxService是否加@Service;2. 检查扫描路径;3. 设置required = false(可选依赖)
报错NoUniqueBeanDefinitionException(同一类型多Bean) 1. 用@Qualifier指定Bean名称;2. 用@Primary标记首选Bean;3. 自定义Bean名称匹配字段名
构造器注入时循环依赖 1. 重构代码解耦;2. 用@Lazy延迟加载其中一个Bean;3. 改用setter注入

2. @Qualifier

核心功能

配合@Autowired使用,按名称(byName)指定要注入的Bean,解决同一类型多Bean的冲突问题。

底层原理
  • 通过QualifierAnnotationAutowireCandidateResolver解析;
  • Spring按类型找到多个Bean后,匹配@Qualifier指定的名称,确定唯一Bean。
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfig {

    @Bean("normalUserService")
    public UserService normalUserService() {
        return new NormalUserService();
    }

    @Bean("vipUserService")
    public UserService vipUserService() {
        return new VipUserService();
    }
}

// 注入时指定名称
package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    @Qualifier("vipUserService") // 精准注入vipUserService
    private UserService userService;
}
使用痛点&解决方案
痛点 解决方案
@Qualifier指定的名称不存在 1. 检查Bean名称是否正确(区分大小写);2. 检查@Bean/@Service的名称配置
同时使用@Primary和@Qualifier @Qualifier优先级更高,会忽略@Primary,按名称注入

3. @Primary

核心功能

标注在Bean上,当同一类型有多个Bean时,该Bean成为@Autowired注入的首选Bean。

底层原理
  • Spring解析@Autowired时,若找到多个同类型Bean,优先选择@Primary标注的Bean;
  • 通过PrimaryAnnotationBeanPostProcessor标记Bean的primary属性为true。
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class DataSourceConfig {

    // 首选数据源(@Autowired默认注入这个)
    @Primary
    @Bean("masterDataSource")
    public DataSource masterDataSource() {
        return new MasterDataSource();
    }

    @Bean("slaveDataSource")
    public DataSource slaveDataSource() {
        return new SlaveDataSource();
    }
}

// 注入时自动选择masterDataSource
package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DataService {

    @Autowired
    private DataSource dataSource; // 注入的是masterDataSource
}
使用痛点&解决方案
痛点 解决方案
同一类型多个@Primary Bean 报错IllegalStateException,确保同一类型只有一个@Primary Bean
@Primary不生效 检查是否同时使用了@Qualifier(优先级更高);检查Bean是否注册成功

4. @Resource

核心功能

JSR-250标准注解,默认按名称(byName)装配Bean,找不到名称时按类型(byType)装配,是@Autowired的替代方案。

底层原理
  1. Spring通过CommonAnnotationBeanPostProcessor解析该注解;
  2. 装配优先级:指定name → 字段名/方法名 → 类型 → 抛出异常。
实际示例
java 复制代码
package com.example.demo.service;

import jakarta.annotation.Resource; // Spring Boot 3+用jakarta
import org.springframework.stereotype.Service;

@Service
public class CartService {

    // 默认按名称装配(字段名cartRepository)
    @Resource
    private CartRepository cartRepository;

    // 指定name属性(精准按名称装配)
    @Resource(name = "vipCartRepository")
    private CartRepository vipCartRepository;

    // 指定type属性(按类型装配,等同于@Autowired)
    @Resource(type = OrderRepository.class)
    private OrderRepository orderRepository;
}
使用痛点&解决方案
痛点 解决方案
Spring Boot 3+中找不到@Resource 替换为jakarta.annotation.Resource;确保引入spring-boot-starter-web(包含jakarta依赖)
按名称找不到,按类型找到多个 1. 指定name属性精准匹配;2. 避免同一类型多个Bean未命名

5. @Scope

核心功能

定义Bean的作用域,控制Bean的创建时机和实例数量。

底层原理
  • Spring通过Scope接口实现作用域管理,不同作用域对应不同的Scope实现类;
  • 单例(singleton):容器启动时创建,全局唯一;
  • 原型(prototype):每次getBean()创建新实例;
  • Web作用域(request/session):绑定到HTTP请求/会话。
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class ScopeConfig {

    // 单例(默认):全局唯一,启动时创建
    @Bean
    @Scope("singleton")
    public SingletonBean singletonBean() {
        return new SingletonBean();
    }

    // 原型:每次getBean()创建新实例
    @Bean
    @Scope("prototype")
    public PrototypeBean prototypeBean() {
        return new PrototypeBean();
    }

    // Web作用域:每个HTTP请求一个实例(需Web环境)
    @Bean
    @Scope("request")
    public RequestBean requestBean() {
        return new RequestBean();
    }

    // 静态内部类
    public static class SingletonBean {}
    public static class PrototypeBean {}
    public static class RequestBean {}
}
使用痛点&解决方案
痛点 解决方案
原型Bean注入到单例Bean中仅创建一次 1. 用ObjectFactory<PrototypeBean>延迟获取;2. 用@Lookup注解;3. 手动调用ApplicationContext.getBean()
request/session作用域在非Web环境报错 1. 仅在Web环境使用;2. 配置@ConditionalOnWebApplication确保仅Web环境加载

6. @Lazy

核心功能

针对单例Bean,延迟Bean的创建时机(从容器启动时推迟到第一次使用时)。

底层原理
  • Spring为@Lazy标注的Bean创建代理对象;
  • 容器启动时仅创建代理,第一次调用Bean方法时才真正创建实例。
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

@Configuration
public class LazyConfig {

    // 延迟创建:第一次使用时才实例化
    @Bean
    @Lazy
    public LazyBean lazyBean() {
        System.out.println("LazyBean创建");
        return new LazyBean();
    }

    // 立即创建(默认):启动时实例化
    @Bean
    public EagerBean eagerBean() {
        System.out.println("EagerBean创建");
        return new EagerBean();
    }

    public static class LazyBean {}
    public static class EagerBean {}
}
使用痛点&解决方案
痛点 解决方案
@Lazy标注后Bean仍启动时创建 1. 检查是否是单例Bean;2. 检查是否有其他Bean提前注入该Bean;3. 检查是否调用了ApplicationContext.getBean()
循环依赖问题未解决 确保两个Bean都加@Lazy;或重构代码解耦

7. @PostConstruct

核心功能

JSR-250标准注解,标注在方法上,在Bean初始化完成后(构造器+@Autowired注入后)执行,用于初始化操作。

底层原理
  • 执行顺序:构造器 → @Autowired注入 → @PostConstruct方法 → Bean就绪;
  • 通过CommonAnnotationBeanPostProcessor解析该注解。
实际示例
java 复制代码
package com.example.demo.service;

import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class CacheService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    // 构造器中无法使用redisTemplate(未注入)
    public CacheService() {
        // System.out.println(redisTemplate); // 报错:NullPointerException
    }

    // 初始化完成后执行:加载数据到缓存
    @PostConstruct
    public void initCache() {
        System.out.println("初始化缓存...");
        redisTemplate.opsForValue().set("init_key", "init_value");
    }
}
使用痛点&解决方案
痛点 解决方案
@PostConstruct方法抛出异常 Bean初始化失败,容器启动报错;1. 捕获异常并处理;2. 确保初始化逻辑稳定
多个@PostConstruct方法执行顺序不确定 1. 合并为一个方法;2. 重构代码逻辑,避免多个初始化方法

8. @PreDestroy

核心功能

JSR-250标准注解,标注在方法上,在Bean销毁前执行,用于释放资源。

底层原理
  • 仅对单例Bean生效;
  • 容器关闭时调用该方法,然后销毁Bean。
实际示例
java 复制代码
package com.example.demo.service;

import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Service;

@Service
public class ResourceService {

    private Connection connection;

    // 初始化连接
    @PostConstruct
    public void initConnection() {
        System.out.println("创建数据库连接...");
        connection = new Connection();
    }

    // 销毁前释放连接
    @PreDestroy
    public void closeConnection() {
        System.out.println("关闭数据库连接...");
        if (connection != null) {
            // connection.close(); // 释放资源
        }
    }

    // 模拟连接类
    private static class Connection {}
}
使用痛点&解决方案
痛点 解决方案
@PreDestroy方法未执行 1. 确保是单例Bean;2. 确保容器正常关闭(如调用ApplicationContext.close());3. 检查是否有异常导致方法未执行
资源释放失败 1. 捕获异常并记录日志;2. 确保资源释放逻辑幂等

四、配置绑定与多环境(外部配置级)

1. @Value

核心功能

从配置文件/环境变量中注入单个值,支持SpEL表达式,适合简单配置。

底层原理
  1. Spring通过AutowiredAnnotationBeanPostProcessor解析该注解;
  2. 解析${key}Environment获取值,解析#{spel}执行表达式。
实际示例
java 复制代码
package com.example.demo.component;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ConfigComponent {

    // 基础用法:注入配置文件中的值
    @Value("${app.name}")
    private String appName;

    // 注入默认值(配置不存在时使用)
    @Value("${app.version:1.0.0}")
    private String appVersion;

    // SpEL表达式:引用其他Bean的属性
    @Value("#{@appConfig.appName + '-test'}")
    private String testAppName;

    public void printConfig() {
        System.out.println("appName: " + appName);
        System.out.println("appVersion: " + appVersion);
    }
}
配置文件(application.yml)
yaml 复制代码
app:
  name: demo-app
使用痛点&解决方案
痛点 解决方案
报错Could not resolve placeholder 'xxx' in value "${xxx}" 1. 检查配置文件中是否存在该key;2. 设置默认值(如${xxx:default});3. 检查配置文件是否加载
注入静态变量失败 1. 不能直接标注静态变量;2. 提供非静态setter方法,标注@Value并赋值给静态变量

2. @ConfigurationProperties

核心功能

批量绑定配置文件中的属性到Java Bean,支持复杂类型(List/Map)和数据校验,是@Value的升级版。

底层原理
  1. Spring通过ConfigurationPropertiesBindingPostProcessor解析该注解;
  2. prefix匹配配置,支持松散绑定(如app.upload-pathuploadPath)。
实际示例
java 复制代码
package com.example.demo.config;

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

import java.util.List;
import java.util.Map;

@ConfigurationProperties(prefix = "app")
@Validated // 启用数据校验
public class AppProperties {

    @NotEmpty(message = "应用名称不能为空")
    private String name;

    private Upload upload;
    private List<String> allowedIps;
    private Map<String, String> envConfig;

    // 嵌套配置
    public static class Upload {
        @Min(value = 1, message = "最小文件大小不能小于1MB")
        @Max(value = 100, message = "最大文件大小不能超过100MB")
        private Integer maxSize;

        @NotEmpty(message = "上传路径不能为空")
        private String path;

        // getter/setter省略
    }

    // getter/setter省略
}
配置文件(application.yml)
yaml 复制代码
app:
  name: demo-app
  upload:
    max-size: 50
    path: /usr/local/upload
  allowed-ips:
    - 127.0.0.1
    - 192.168.1.1
  env-config:
    dev: development
    prod: production
注册配置类
java 复制代码
@Configuration
@EnableConfigurationProperties(AppProperties.class)
public class ConfigRegister {
}
使用痛点&解决方案
痛点 解决方案
绑定失败(字段为null) 1. 检查prefix是否正确;2. 检查字段名是否支持松散绑定;3. 确保有setter方法;4. 加@EnableConfigurationProperties
数据校验不生效 1. 加@Validated注解;2. 引入spring-boot-starter-validation依赖;3. 检查校验注解是否正确(jakarta.validation.constraints)

3. @PropertySource

核心功能

加载自定义.properties配置文件,将配置项加入Spring Environment。

底层原理
  • 通过PropertySourceAnnotationBeanPostProcessor加载指定文件;
  • 将文件中的键值对封装为PropertySource,注册到Environment
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

// 加载自定义配置文件,指定编码
@PropertySource(value = "classpath:custom.properties", encoding = "UTF-8")
@Configuration
public class CustomConfig {
}
自定义配置文件(custom.properties)
properties 复制代码
custom.name=自定义配置
custom.age=20
使用痛点&解决方案
痛点 解决方案
加载YAML文件失败 1. @PropertySource默认不支持YAML;2. 自定义YAML加载器(实现PropertySourceFactory);3. 改用application.yml的多环境配置
配置文件找不到 1. 检查文件路径(classpath:/xxx.properties);2. 确保文件在resources目录下

4. @Profile

核心功能

指定Bean/配置类仅在特定环境(dev/test/prod)下生效,实现多环境配置隔离。

底层原理
  • 通过ProfileCondition条件判断;
  • 读取spring.profiles.active配置,匹配则加载Bean,否则跳过。
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class EnvConfig {

    // 开发环境数据源
    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        System.out.println("加载开发环境数据源");
        return new DevDataSource();
    }

    // 生产环境数据源
    @Bean
    @Profile({"prod", "default"})
    public DataSource prodDataSource() {
        System.out.println("加载生产环境数据源");
        return new ProdDataSource();
    }

    // 静态内部类
    public static class DevDataSource {}
    public static class ProdDataSource {}
}
激活环境(application.yml)
yaml 复制代码
spring:
  profiles:
    active: dev # 激活开发环境
使用痛点&解决方案
痛点 解决方案
无匹配的Profile时Bean加载失败 1. 指定默认Profile(如@Profile("default"));2. 确保spring.profiles.active配置正确
多Profile激活时Bean冲突 1. 确保不同Profile的Bean名称不同;2. 避免同时激活冲突的Profile

5. @Validated

核心功能

开启参数校验,支持分组校验,可配合@ConfigurationProperties或Controller参数校验。

底层原理
  • 基于JSR-380(jakarta.validation)实现;
  • 通过MethodValidationPostProcessor解析注解,触发校验。
实际示例
java 复制代码
package com.example.demo.controller;

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Validated // 开启Controller参数校验
public class ValidateController {

    @PostMapping("/user/save")
    public String saveUser(@RequestBody @Validated UserVO user) {
        return "保存用户:" + user.getName();
    }

    // 分组校验
    @PostMapping("/user/update")
    public String updateUser(@RequestBody @Validated(UpdateGroup.class) UserVO user) {
        return "更新用户:" + user.getId();
    }

    // 分组接口
    public interface UpdateGroup {}

    // 用户VO
    public static class UserVO {
        @NotNull(groups = UpdateGroup.class, message = "用户ID不能为空")
        private Long id;

        @NotEmpty(message = "用户名不能为空")
        private String name;

        // getter/setter省略
    }
}
使用痛点&解决方案
痛点 解决方案
校验不生效 1. 加@Validated注解;2. 引入spring-boot-starter-validation依赖;3. 确保参数前加@Validated/@Valid
分组校验不生效 1. 确保分组接口正确;2. 校验注解指定分组;3. 参数前指定分组(如@Validated(UpdateGroup.class)

五、Web MVC 接口开发(HTTP 请求级)

5.1 请求映射

1. @RequestMapping
核心功能

基础请求映射注解,绑定HTTP请求路径到控制器方法,支持指定请求方法、请求头、参数等。

底层原理
  • Spring MVC通过RequestMappingHandlerMapping扫描该注解,生成RequestMappingInfo
  • 请求到来时,匹配RequestMappingInfo找到对应的处理器方法。
实际示例
java 复制代码
package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user") // 类级别路径
public class UserController {

    // 匹配GET请求
    @RequestMapping(value = "/info", method = RequestMethod.GET)
    public String getUserInfo() {
        return "用户信息";
    }

    // 多路径映射
    @RequestMapping(value = {"/save", "/create"}, method = RequestMethod.POST)
    public String saveUser() {
        return "保存用户";
    }
}
使用痛点&解决方案
痛点 解决方案
请求路径冲突 1. 区分请求方法(GET/POST);2. 增加请求头/参数条件;3. 调整路径命名
Ant风格路径匹配错误 1. 明确规则(*匹配一级,**匹配多级);2. 避免过度使用通配符
2. @GetMapping / @PostMapping / @PutMapping / @DeleteMapping / @PatchMapping
核心功能

@RequestMapping的简化版,分别对应GET/POST/PUT/DELETE/PATCH请求,语义更清晰。

底层原理
  • 底层继承@RequestMapping,仅限定请求方法;
  • 其他原理与@RequestMapping一致。
实际示例
java 复制代码
package com.example.demo.controller;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/order")
public class OrderController {

    // 查询(GET)
    @GetMapping("/{id}")
    public String getOrder(@PathVariable Long id) {
        return "查询订单:" + id;
    }

    // 新增(POST)
    @PostMapping
    public String createOrder(@RequestBody OrderVO order) {
        return "创建订单:" + order.getOrderNo();
    }

    // 全量更新(PUT)
    @PutMapping("/{id}")
    public String updateOrder(@PathVariable Long id, @RequestBody OrderVO order) {
        return "更新订单:" + id;
    }

    // 删除(DELETE)
    @DeleteMapping("/{id}")
    public String deleteOrder(@PathVariable Long id) {
        return "删除订单:" + id;
    }

    // 部分更新(PATCH)
    @PatchMapping("/{id}/status")
    public String updateOrderStatus(@PathVariable Long id, @RequestParam Integer status) {
        return "更新订单" + id + "状态为:" + status;
    }

    public static class OrderVO {
        private String orderNo;
        // getter/setter省略
    }
}
使用痛点&解决方案
痛点 解决方案
PUT/PATCH/DELETE请求被拦截 1. 配置跨域(@CrossOrigin);2. 配置Spring Security允许这些方法;3. 前端用X-HTTP-Method-Override模拟
PATCH请求接收参数复杂 1. 用@RequestBody接收JSON;2. 避免传递过多参数

5.2 参数接收

1. @PathVariable
核心功能

从URL路径模板中提取变量(如/user/{id}),绑定到方法参数。

底层原理
  • 通过PathVariableMethodArgumentResolver解析;
  • 提取路径变量值,类型转换后绑定到参数。
实际示例
java 复制代码
package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/product")
public class ProductController {

    // 基础用法:参数名与路径变量名一致
    @GetMapping("/info/{id}")
    public String getProductInfo(@PathVariable Long id) {
        return "商品ID:" + id;
    }

    // 参数名与路径变量名不一致
    @GetMapping("/name/{productName}")
    public String getProductName(@PathVariable("productName") String name) {
        return "商品名称:" + name;
    }

    // 可选路径变量(Spring 5.3+)
    @GetMapping("/optional/{id:\\d+}?")
    public String getOptionalProduct(@PathVariable(required = false) Long id) {
        return id == null ? "无商品ID" : "商品ID:" + id;
    }
}
使用痛点&解决方案
痛点 解决方案
类型转换失败(如String→Long) 1. 确保路径变量格式正确;2. 用正则限制格式(如{id:\\d+});3. 全局异常处理转换异常
可选路径变量不生效 1. 确保Spring版本≥5.3;2. 路径模板后加?(如/{id}?);3. 设置required = false
2. @RequestParam
核心功能

从HTTP请求中提取查询参数(URL中的?key=value)或表单数据,绑定到方法参数。

底层原理
  • 通过RequestParamMethodArgumentResolver解析;
  • HttpServletRequest.getParameterMap()获取值,支持多值参数(List)。
实际示例
java 复制代码
package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/search")
public class SearchController {

    // 必填参数
    @GetMapping("/user")
    public String searchUser(@RequestParam String name) {
        return "搜索用户:" + name;
    }

    // 可选参数+默认值
    @GetMapping("/product")
    public String searchProduct(
            @RequestParam(required = false, defaultValue = "手机") String keyword,
            @RequestParam(defaultValue = "1") Integer pageNum) {
        return "关键词:" + keyword + ",页码:" + pageNum;
    }

    // 多值参数
    @GetMapping("/batch")
    public String searchBatch(@RequestParam List<Long> ids) {
        return "批量搜索ID:" + ids;
    }
}
使用痛点&解决方案
痛点 解决方案
必选参数缺失 1. 设置required = false;2. 提供默认值;3. 全局异常处理返回友好提示
多值参数接收不到 1. 前端传递多个相同参数名(如ids=1&ids=2);2. 用逗号分隔字符串,后端拆分
3. @RequestBody
核心功能

从HTTP请求体中读取JSON/XML数据,转换为Java对象,绑定到方法参数。

底层原理
  • 通过RequestResponseBodyMethodProcessor解析;
  • 根据Content-Type选择HttpMessageConverter(如Jackson处理JSON),反序列化为Java对象。
实际示例
java 复制代码
package com.example.demo.controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api")
public class JsonController {

    // 接收单个对象
    @PostMapping("/user")
    public String receiveUser(@RequestBody UserVO user) {
        return "接收用户:" + user.getName();
    }

    // 接收数组
    @PostMapping("/users")
    public String receiveUsers(@RequestBody List<UserVO> users) {
        return "接收" + users.size() + "个用户";
    }

    public static class UserVO {
        private String name;
        private Integer age;
        // getter/setter省略
    }
}
使用痛点&解决方案
痛点 解决方案
接收不到JSON数据 1. 检查Content-Type是否为application/json;2. 检查JSON格式;3. 检查实体类有setter方法;4. 用@JsonProperty匹配字段名
日期类型转换失败 1. 配置Jackson日期格式;2. 实体类字段加@JsonFormat(pattern = "yyyy-MM-dd")
4. @RequestHeader / @CookieValue / @ModelAttribute
核心功能
  • @RequestHeader:获取请求头信息;
  • @CookieValue:获取Cookie值;
  • @ModelAttribute:绑定请求参数到对象,或预处理数据。
实际示例
java 复制代码
package com.example.demo.controller;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/header")
public class HeaderController {

    // 获取请求头
    @GetMapping("/user-agent")
    public String getUserAgent(@RequestHeader("User-Agent") String userAgent) {
        return "User-Agent:" + userAgent;
    }

    // 获取Cookie
    @GetMapping("/cookie")
    public String getCookie(@CookieValue(value = "JSESSIONID", required = false) String sessionId) {
        return "JSESSIONID:" + sessionId;
    }

    // @ModelAttribute预处理数据
    @ModelAttribute
    public void preProcess(@RequestParam(required = false) String token, Model model) {
        model.addAttribute("token", token == null ? "default-token" : token);
    }

    // 使用预处理数据
    @GetMapping("/model")
    public String getModel(Model model) {
        return "Token:" + model.getAttribute("token");
    }
}

5.3 响应与跨域

1. @ResponseBody
核心功能

将方法返回值直接写入HTTP响应体(JSON/XML),而非解析为视图路径。

底层原理
  • 通过HttpMessageConverter转换返回值;
  • @RestController已包含该注解,无需重复添加。
2. @CrossOrigin
核心功能

开启跨域访问支持,解决前后端分离中的跨域问题。

实际示例
java 复制代码
package com.example.demo.controller;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

// 类级别:允许所有域名跨域
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/cors")
public class CorsController {

    // 方法级别:仅允许指定域名
    @CrossOrigin(origins = "http://localhost:8080")
    @GetMapping("/info")
    public String getInfo() {
        return "跨域访问成功";
    }
}
使用痛点&解决方案
痛点 解决方案
跨域仍报错 1. 检查origins是否配置正确;2. 确保maxAge设置合理;3. 全局配置跨域(替代注解)

六、全局统一处理(AOP & 异常 & 增强)

1. @ControllerAdvice / @RestControllerAdvice

核心功能

全局控制器增强,@RestControllerAdvice = @ControllerAdvice + @ResponseBody,用于全局异常处理、数据绑定、预处理。

底层原理
  • @Component标注,会被Spring扫描为Bean;
  • Spring MVC初始化时,提取其中的@ExceptionHandler/@InitBinder/@ModelAttribute方法,注册为全局处理器。
实际示例(全局异常处理)
java 复制代码
package com.example.demo.advice;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

// 全局异常处理,返回JSON
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 处理参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidException(MethodArgumentNotValidException e) {
        Map<String, String> errors = new HashMap<>();
        e.getBindingResult().getFieldErrors().forEach(error -> {
            errors.put(error.getField(), error.getDefaultMessage());
        });
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }

    // 处理通用异常
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleException(Exception e) {
        return new ResponseEntity<>("服务器内部错误:" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
使用痛点&解决方案
痛点 解决方案
全局异常处理不生效 1. 检查是否加@RestControllerAdvice/@ControllerAdvice;2. 检查异常类型是否匹配;3. 检查扫描路径是否包含该类
特定Controller不适用全局处理 1. 在Controller内定义@ExceptionHandler(优先级更高);2. 用@RestControllerAdvice(basePackages = "com.example.demo.controller")限定范围

2. @ExceptionHandler

核心功能

标注在方法上,声明该方法用于处理指定类型的异常,通常配合@ControllerAdvice使用。

实际示例

@RestControllerAdvice示例。

3. @Aspect / @Before / @After / @Around / @Pointcut

核心功能

AOP核心注解,实现切面编程,无侵入式增强方法(如日志、性能监控、权限校验)。

底层原理
  • @Aspect标记切面类;
  • @Pointcut定义切点表达式;
  • @Before/@After/@Around定义通知(增强逻辑)。
实际示例(接口性能监控)
java 复制代码
package com.example.demo.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect // 标记切面类
@Component
public class PerformanceAspect {

    // 定义切点:所有Controller的方法
    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
    public void controllerPointcut() {}

    // 环绕通知:监控方法执行时间
    @Around("controllerPointcut()")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        // 执行目标方法
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        // 打印耗时
        System.out.println(joinPoint.getSignature() + " 执行耗时:" + (end - start) + "ms");
        return result;
    }
}
使用痛点&解决方案
痛点 解决方案
切面不生效 1. 检查是否加@Aspect@Component;2. 检查切点表达式是否正确;3. 确保目标方法是public(AOP不支持private)
内部调用切面不生效 1. 通过代理对象调用方法;2. 启用@EnableAspectJAutoProxy(exposeProxy = true),用AopContext.currentProxy()获取代理

七、数据访问与事务(数据库级)

1. @Transactional

核心功能

声明式事务管理核心注解,标注在类/方法上,开启数据库事务。

底层原理
  • 通过Spring AOP实现,生成代理对象;
  • 方法执行前开启事务,执行后提交,异常时回滚。
实际示例
java 复制代码
package com.example.demo.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderService {

    // 类级别:所有方法都开启事务
    // @Transactional
    private final OrderRepository orderRepository;

    public OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    // 方法级别:覆盖类级别配置
    @Transactional(
            rollbackFor = Exception.class, // 所有异常都回滚(默认仅RuntimeException)
            propagation = Propagation.REQUIRED, // 默认传播行为
            isolation = Isolation.READ_COMMITTED // 隔离级别
    )
    public void createOrder(OrderVO order) {
        // 业务逻辑:新增订单 + 更新库存
        orderRepository.save(order);
        // 模拟异常:触发回滚
        // throw new RuntimeException("创建订单失败");
    }
}
使用痛点&解决方案
痛点 解决方案
事务失效 1. 方法非public(AOP不支持);2. 同类内部调用(未走代理);3. 异常被捕获未抛出;4. 异常类型不匹配(默认仅RuntimeException);5. 数据库引擎不支持事务(如MyISAM)
事务回滚不全 1. 指定rollbackFor = Exception.class;2. 确保所有操作在同一个事务中;3. 检查是否有非事务操作(如调用外部接口)

2. JPA 注解(@Entity / @Table / @Id / @GeneratedValue 等)

核心功能

JPA实体映射核心注解,实现Java对象与数据库表的映射。

实际示例
java 复制代码
package com.example.demo.entity;

import jakarta.persistence.*;
import java.util.Date;

@Entity // 标记为JPA实体
@Table(name = "t_user") // 映射数据库表名
public class User {

    @Id // 主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增策略
    private Long id;

    @Column(name = "user_name", length = 50, nullable = false) // 字段映射
    private String userName;

    @Temporal(TemporalType.DATE) // 日期精度
    private Date createTime;

    @Transient // 不映射到数据库
    private String tempField;

    // getter/setter省略
}
使用痛点&解决方案
痛点 解决方案
实体映射失败 1. 检查注解是否正确(jakarta.persistence而非javax);2. 检查表名/字段名是否匹配;3. 确保有无参构造器
主键生成策略失效 1. 检查数据库是否支持自增(如MySQL支持IDENTITY,Oracle支持SEQUENCE);2. 确保主键字段类型匹配

3. MyBatis 注解(@Mapper / @MapperScan / @Select 等)

核心功能

MyBatis核心注解,实现SQL映射,替代XML文件。

实际示例
java 复制代码
package com.example.demo.mapper;

import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper // 标记为MyBatis Mapper接口
public interface UserMapper {

    // 查询
    @Select("SELECT * FROM t_user WHERE id = #{id}")
    User findById(Long id);

    // 新增:获取自增主键
    @Insert("INSERT INTO t_user(user_name) VALUES (#{userName})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);

    // 更新
    @Update("UPDATE t_user SET user_name = #{userName} WHERE id = #{id}")
    int update(User user);

    // 删除
    @Delete("DELETE FROM t_user WHERE id = #{id}")
    int delete(Long id);

    // 多参数:用@Param指定名称
    @Select("SELECT * FROM t_user WHERE user_name = #{name} AND age = #{age}")
    List<User> findByCondition(@Param("name") String name, @Param("age") Integer age);
}
扫描Mapper(启动类)
java 复制代码
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 扫描所有Mapper接口
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
使用痛点&解决方案
痛点 解决方案
Mapper注入失败 1. 检查是否加@Mapper@MapperScan;2. 检查扫描路径是否正确;3. 确保Mapper是接口(非类)
多参数绑定失败 1. 用@Param指定参数名称;2. 用Map传递参数;3. 用实体类封装参数

八、高级能力:异步、定时、消息

1. @EnableAsync / @Async

核心功能

开启异步方法执行能力,标记方法为异步方法,调用后立即返回,逻辑在线程池中执行。

底层原理
  • @EnableAsync开启异步支持;
  • @Async标注的方法被AOP代理,提交到线程池执行。
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@EnableAsync // 开启异步支持
public class AsyncConfig {

    // 自定义线程池(避免使用默认的SimpleAsyncTaskExecutor)
    @Bean("asyncExecutor")
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); // 核心线程数
        executor.setMaxPoolSize(10); // 最大线程数
        executor.setQueueCapacity(20); // 队列容量
        executor.setThreadNamePrefix("async-");
        executor.initialize();
        return executor;
    }
}

// 异步方法
package com.example.demo.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {

    // 使用自定义线程池
    @Async("asyncExecutor")
    public void asyncTask() {
        // 耗时操作:如发送邮件、生成报表
        System.out.println("异步任务执行中,线程:" + Thread.currentThread().getName());
    }
}
使用痛点&解决方案
痛点 解决方案
异步方法失效 1. 未加@EnableAsync;2. 同类内部调用(未走代理);3. 方法非public;4. 返回值为void时异常无法捕获
线程池耗尽 1. 自定义线程池,设置合理参数;2. 监控线程池状态;3. 避免提交大量异步任务

2. @EnableScheduling / @Scheduled

核心功能

开启定时任务支持,标记方法为定时任务,支持cron表达式、固定频率、固定延迟。

底层原理
  • @EnableScheduling开启定时任务;
  • @Scheduled标注的方法被TaskScheduler调度执行,默认单线程池。
实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

import java.util.concurrent.ScheduledExecutorService;

@Configuration
@EnableScheduling // 开启定时任务
public class SchedulerConfig {

    // 自定义定时任务线程池(避免单线程阻塞)
    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(5);
        scheduler.setThreadNamePrefix("scheduler-");
        return scheduler;
    }
}

// 定时任务
package com.example.demo.service;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class ScheduledService {

    // cron表达式:每分钟执行一次
    @Scheduled(cron = "0 * * * * ?")
    public void cronTask() {
        System.out.println("Cron任务执行:" + System.currentTimeMillis());
    }

    // 固定频率:每隔5秒执行(以上次开始时间计算)
    @Scheduled(fixedRate = 5000)
    public void fixedRateTask() {
        System.out.println("固定频率任务执行");
    }

    // 固定延迟:每隔5秒执行(以上次结束时间计算)
    @Scheduled(fixedDelay = 5000)
    public void fixedDelayTask() {
        System.out.println("固定延迟任务执行");
    }
}
使用痛点&解决方案
痛点 解决方案
定时任务阻塞 1. 自定义线程池(ThreadPoolTaskScheduler);2. 避免任务执行时间过长;3. 异步执行耗时任务
cron表达式错误 1. 在线工具验证cron表达式;2. 注意秒/分/时/日/月/周的顺序;3. 避免日和周同时指定

3. 消息队列注解(@RabbitListener / @KafkaListener)

核心功能

标记方法为消息队列消费者,监听指定队列/主题,消息到达时自动执行。

实际示例(RabbitMQ)
java 复制代码
package com.example.demo.listener;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class RabbitMQListener {

    // 监听指定队列
    @RabbitListener(queues = "demo.queue")
    public void receiveMessage(String message) {
        System.out.println("接收RabbitMQ消息:" + message);
    }
}

九、条件装配(Spring Boot 自动配置灵魂)

1. @Conditional / @ConditionalOnClass / @ConditionalOnMissingBean 等

核心功能

根据条件决定是否加载Bean,是Spring Boot自动配置的核心机制。

实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ConditionConfig {

    // 类路径下存在RedisTemplate时生效
    @ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
    @Bean
    public RedisService redisService() {
        return new RedisService();
    }

    // 容器中不存在MyService时才创建
    @ConditionalOnMissingBean(MyService.class)
    @Bean
    public MyService defaultMyService() {
        return new DefaultMyService();
    }

    // 配置文件中my.feature.enabled=true时生效
    @ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true")
    @Bean
    public FeatureService featureService() {
        return new FeatureService();
    }

    // 静态内部类
    public static class RedisService {}
    public static class MyService {}
    public static class DefaultMyService extends MyService {}
    public static class FeatureService {}
}
使用痛点&解决方案
痛点 解决方案
条件装配不生效 1. 检查条件是否满足(如类是否存在、配置是否正确);2. 启动日志添加debug: true查看条件匹配结果;3. 确保条件注解标注在@Bean方法/配置类上

十、安全与权限

1. @EnableWebSecurity / @EnableGlobalMethodSecurity

核心功能

开启Spring Security Web安全支持和方法级权限控制。

实际示例
java 复制代码
package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity // 开启Web安全
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启@PreAuthorize
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll() // 公开接口
                .anyRequest().authenticated() // 其他接口需认证
            )
            .formLogin(); // 启用表单登录
        return http.build();
    }
}

// 方法级权限
package com.example.demo.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin")
public class AdminController {

    // 仅ADMIN角色可访问
    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/info")
    public String adminInfo() {
        return "管理员信息";
    }

    // 支持SpEL表达式,更复杂的权限校验
    @PreAuthorize("authentication.name == #username")
    @GetMapping("/user/{username}")
    public String userInfo(@PathVariable String username) {
    # 十、安全与权限(续)
### 4. @PreAuthorize
#### 核心功能
方法执行**前**进行权限校验,支持 Spring EL 表达式,是 Spring Security 最常用、最灵活的权限注解。
#### 底层原理
- 需配合 `@EnableGlobalMethodSecurity(prePostEnabled = true)` 开启;
- Spring AOP 拦截方法调用,执行 EL 表达式,返回 false 则拒绝执行并抛异常。
#### 实际示例
```java
@RestController
@RequestMapping("/user")
public class UserController {

    // 必须有 ADMIN 角色才能访问
    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/list")
    public List<User> list() {
        return userService.list();
    }

    // 只能查看自己的信息:登录用户名 == 路径参数 username
    @PreAuthorize("authentication.name == #username")
    @GetMapping("/info/{username}")
    public User info(@PathVariable String username) {
        return userService.getByUsername(username);
    }

    // 拥有某个权限
    @PreAuthorize("hasAuthority('user:delete')")
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) {
        userService.delete(id);
    }
}
高频痛点 & 解决方案
  • 权限表达式不生效:
    检查是否开启 prePostEnabled = true
  • EL 表达式写错导致一直拒绝:
    优先使用 hasRole/hasAuthority,少手写复杂逻辑;
  • 匿名访问也进入校验报错:
    接口放行写在 SecurityFilterChain 里,不要依赖注解。

5. @PostAuthorize

核心功能

方法执行后 再做权限校验,常用于校验返回结果是否允许当前用户查看。

底层原理
  • 方法先执行完,再执行 EL 表达式;
  • 不满足则抛异常,不会回滚业务,只阻止结果返回。
实际示例
java 复制代码
@Service
public class OrderService {

    // 返回订单的用户名 == 当前登录用户才允许返回
    @PostAuthorize("returnObject.username == authentication.name")
    public Order getOrder(Long orderId) {
        return orderMapper.selectById(orderId);
    }
}
高频痛点 & 解决方案
  • 数据已经查出来/修改完才被拒绝:
    只适合查询,不适合写操作;
  • 性能差:
    优先用 @PreAuthorize,少用 @PostAuthorize

6. @AuthenticationPrincipal

核心功能

在 Controller 方法上直接获取当前登录用户信息(UserDetails 或自定义用户对象)。

底层原理
  • Spring Security 从认证上下文 SecurityContextHolder 中取出用户主体;
  • 自动绑定到方法参数。
实际示例
java 复制代码
@GetMapping("/me")
public R currentUser(@AuthenticationPrincipal UserDetails userDetails) {
    return R.ok(userDetails.getUsername());
}

// 如果你自定义了 LoginUser 实现 UserDetails,可以直接写
@GetMapping("/me")
public R me(@AuthenticationPrincipal LoginUser loginUser) {
    return R.ok(loginUser.getUser());
}
高频痛点 & 解决方案
  • 获取为 null:
    接口未登录 / 过滤器未正确将用户放入上下文;
  • 无法获取自定义字段:
    自定义 UserDetails 并在登录时完整赋值。

7. @Secured

核心功能

简单的角色校验 ,老版本 Spring Security 常用,不支持 EL 表达式

底层原理
  • 需开启 @EnableGlobalMethodSecurity(securedEnabled = true)
  • 仅判断是否拥有指定角色。
实际示例
java 复制代码
@Secured("ROLE_ADMIN")
@GetMapping("/admin")
public String admin() {
    return "admin page";
}
高频痛点 & 解决方案
  • 功能太弱,不支持复杂判断:
    新项目直接用 @PreAuthorize 替代。

8. @RolesAllowed

核心功能

Java JSR250 标准注解,作用同 @Secured标准通用,不依赖 Spring。

实际示例
java 复制代码
@RolesAllowed("ADMIN")
@GetMapping("/manage")
public String manage() {
    return "manage";
}

十一、测试与切片注解(单元/集成测试)

这一组专门用于不启动整个项目、只测某一层,极大提升测试速度。

1. @SpringBootTest

核心功能

启动完整 Spring 容器 ,做集成测试,接近真实运行环境。

底层原理
  • 加载 @SpringBootApplication 启动类;
  • 初始化几乎所有 Bean、配置、自动配置。
实际示例
java 复制代码
@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testList() {
        List<User> list = userService.list();
        Assert.notNull(list, "列表不能为空");
    }
}
高频痛点 & 解决方案
  • 启动极慢:
    只在全链路集成测试用,单元测试用切片注解。

2. @WebMvcTest

核心功能

只启动 Web 层(Controller),不加载 Service、Repository,专门测接口。

底层原理
  • 只实例化:Controller、HandlerInterceptor、Filter、MessageConverter 等;
  • 依赖的 Service 必须用 @MockBean 模拟。
实际示例
java 复制代码
@WebMvcTest(UserController.class)
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    public void testInfo() throws Exception {
        // 模拟 Service 返回
        User user = new User();
        user.setId(1L);
        user.setUsername("test");
        Mockito.when(userService.getById(1L)).thenReturn(user);

        // 模拟请求
        mockMvc.perform(MockMvcRequestBuilders.get("/user/1"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("username").value("test"));
    }
}
高频痛点 & 解决方案
  • Service 注入失败:
    必须用 @MockBean 模拟,不能真正注入。

3. @DataJpaTest

核心功能

只启动数据访问层,专门测试 Repository / JPA。

底层原理
  • 自动配置数据源、JPA、Repository;
  • 默认使用嵌入式数据库 (H2),测试完自动回滚,不污染真实库。
实际示例
java 复制代码
@DataJpaTest
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void testSave() {
        User user = new User();
        user.setUsername("test");
        User saved = userRepository.save(user);
        Assert.notNull(saved.getId(), "保存后ID不能为空");
    }
}
高频痛点 & 解决方案
  • 不想用 H2,想用真实库:
    添加 @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)

4. @MockBean

核心功能

向 Spring 容器中放入一个模拟对象,替换真实 Bean,用于隔离依赖。

底层原理
  • 基于 Mockito;
  • 生成代理对象,不会执行真实逻辑。
实际示例
java 复制代码
@SpringBootTest
public class OrderServiceTest {

    @Autowired
    private OrderService orderService;

    @MockBean
    private UserService userService; // 用模拟对象替换

    @Test
    public void testCreate() {
        // 模拟 userService 返回
        Mockito.when(userService.getById(1L)).thenReturn(new User());

        orderService.create(1L);
    }
}
高频痛点 & 解决方案
  • 模拟不生效:
    确保是 @MockBean(Spring 注解),不是 @Mock(纯 Mockito)。

5. @SpyBean

核心功能

半真半假 :真实对象,可部分模拟 ,没被 mock 的方法会真实执行

实际示例
java 复制代码
@SpyBean
private UserService userService;

@Test
public void testSpy() {
    // 只模拟 getById,其他方法真实走业务
    Mockito.when(userService.getById(1L)).thenReturn(new User());
}

6. @Sql

核心功能

测试执行前后自动跑 SQL 脚本,用于初始化测试数据。

实际示例
java 复制代码
@SpringBootTest
// 执行测试前跑脚本
@Sql(scripts = "classpath:init-test-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
public class SqlTest {

    @Test
    public void testQuery() {
        // 数据库已有初始化数据
    }
}

7. @TestPropertySource

核心功能

给测试单独指定配置文件,不与正式环境混用。

实际示例
java 复制代码
@SpringBootTest
@TestPropertySource("classpath:application-test.yml")
public class ConfigTest {
}

十二、日志、缓存、接口文档(常用扩展注解)

1. @Slf4j

核心功能

Lombok 提供,自动生成 log 常量 ,不用手写 private final Logger log = ...

实际示例
java 复制代码
@Service
@Slf4j
public class UserService {
    public void list() {
        log.info("查询用户列表");
    }
}

2. @Cacheable / @CacheEvict / @CachePut

核心功能

Spring 声明式缓存,简化 Redis 等缓存操作。

实际示例
java 复制代码
// 查询:缓存不存在则执行方法,结果存入缓存
@Cacheable(value = "user", key = "#id")
public User getById(Long id) {
    return userMapper.selectById(id);
}

// 更新:更新数据库,并更新缓存
@CachePut(value = "user", key = "#user.id")
public User update(User user) {
    userMapper.updateById(user);
    return user;
}

// 删除:删除缓存
@CacheEvict(value = "user", key = "#id")
public void delete(Long id) {
    userMapper.deleteById(id);
}

3. 接口文档(SpringDoc / Knife4j)

java 复制代码
@Tag(name = "用户管理")
@RestController
@RequestMapping("/user")
public class UserController {

    @Operation(summary = "获取用户详情")
    @Parameter(name = "id", description = "用户ID")
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id) {
        return userService.getById(id);
    }
}

整套注解逻辑总结(背会这一段,Spring Boot 彻底通)

  1. 先启动@SpringBootApplication 全家桶
  2. 再装 Bean@Configuration + @Bean@Service@Component
  3. 再注入@Autowired@Resource@Qualifier
  4. 读配置@Value@ConfigurationProperties@Profile
  5. 写接口@RestController@GetMapping@RequestBody
  6. 统一处理@RestControllerAdvice@ExceptionHandler@Aspect
  7. 数据库@Transactional@Mapper@Entity
  8. 高级能力@Async@Scheduled@RabbitListener
  9. 条件加载@ConditionalOnXxx
  10. 安全@PreAuthorize
  11. 测试@WebMvcTest@MockBean
相关推荐
xiaoye37082 小时前
Spring 的自动装配 vs 手动注入
java·spring
ShoreKiten2 小时前
Flask/ssti --by vulhub
后端·python·flask
彭于晏Yan2 小时前
Spring Boot监听Redis Key过期事件
java·spring boot·redis
weixin_418270532 小时前
window上codex 安装
java
低调小一2 小时前
OpenClaw 模型配置与火山 Coding Plan 支持清单(实践笔记)
java·前端·笔记·openclaw
xuxie992 小时前
Next 13 sqlite3 查找、网页
java·数据库·oracle
像少年啦飞驰点、2 小时前
Java策略模式从入门到实战:小白也能看懂的设计模式指南
java·设计模式·策略模式·编程入门·小白教程
꯭ 瞎꯭扯꯭蛋꯭2 小时前
3万字80道Java基础经典面试题总结
java·开发语言
程序员Terry2 小时前
别再用 if-else 堆砌代码了!策略模式让你的代码优雅十倍
java·设计模式