Article

Spring 文档

更新于:2026-07-15

第一章:Spring 框架基础与 IoC 容器

1.1 Spring 简介与核心概念

方法名语法用途代码示例注意事项
getBeanObject getBean(String name)从 Spring 容器中获取指定名称的 Bean 实例context.getBean("userService")返回的是 Object 类型,需手动强转;建议使用泛型版本
getBean (泛型)<T> T getBean(Class<T> requiredType)根据类型从容器中获取 Bean 实例UserService service = context.getBean(UserService.class)推荐使用此方式,类型安全,无需强转
getBean (带参数)Object getBean(String name, Object... args)获取带有构造参数或工厂参数的 Beancontext.getBean("orderService", orderId)用于动态传参创建 Bean,适用于原型作用域

1.2 控制反转(IoC)与依赖注入(DI)

方法名语法用途代码示例注意事项
setApplicationContextvoid setApplicationContext(ApplicationContext applicationContext) throws BeansException实现 ApplicationContextAware 接口后可获取上下文public void setApplicationContext(...) { this.context = applicationContext; }用于非 Bean 类中获取 Spring 容器,慎用,破坏 IoC 原则
@DependsOn@DependsOn("beanName")指定 Bean 的初始化顺序@Component @DependsOn("dataSource") class DataInitializer { }保证被依赖的 Bean 先于当前 Bean 初始化
setBeanNamevoid setBeanName(String name)实现 BeanNameAware 接口获取 Bean 的名称public void setBeanName(String name) { this.beanName = name; }用于调试或日志记录,了解当前 Bean 名称

1.3 Bean 的定义与配置方式(XML 与 注解)

方法名语法用途代码示例注意事项
registerBeanDefinitionvoid registerBeanDefinition(String beanName, BeanDefinition beanDefinition)动态向容器注册 Bean 定义beanDefinitionRegistry.registerBeanDefinition("myBean", bd)通常在 BeanFactoryPostProcessor 中使用,不推荐运行时频繁调用
loadBeanDefinitionsint loadBeanDefinitions(Resource resource)从 XML 资源加载 Bean 定义xmlBeanFactory.loadBeanDefinitions(new ClassPathResource("beans.xml"))传统方式,现多用于兼容老项目
scanvoid scan(String... basePackages)扫描指定包下的注解类并注册为 BeanClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry); scanner.scan("com.example");用于自定义扫描逻辑,Spring Boot 自动配置中常见

1.4 ApplicationContext 与 BeanFactory

方法名语法用途代码示例注意事项
getBean<T> T getBean(Class<T> requiredType)从容器中获取指定类型的 Bean 实例UserService userService = context.getBean(UserService.class);ApplicationContext 推荐使用此方法,类型安全
containsBeanboolean containsBean(String name)检查容器中是否包含指定名称的 Beanif (context.containsBean("dataSource")) { ... }可用于条件初始化逻辑,避免 NoSuchBeanDefinitionException
isSingletonboolean isSingleton(String name)判断指定名称的 Bean 是否为单例boolean isSingle = context.isSingleton("service");注意:原型(prototype)Bean 每次获取都是新实例
isPrototypeboolean isPrototype(String name)判断指定名称的 Bean 是否为原型作用域boolean isProto = context.isPrototype("task");与 isSingleton 互斥,仅用于判断作用域
getTypeClass<?> getType(String name)获取指定名称 Bean 的类型Class<?> clazz = context.getType("userRepository");可能返回 null(如 FactoryBean 或未加载)
getAliasesString[] getAliases(String name)获取指定 Bean 名称的所有别名String[] aliases = context.getAliases("userService");一个 Bean 可有多个别名,用于逻辑解耦或测试

1.5 Bean 的作用域与生命周期

方法名语法用途代码示例注意事项
setScopevoid setScope(String scopeName)设置 Bean 的作用域(在 BeanDefinition 中)beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);通常在 BeanFactoryPostProcessor 中修改,不可运行时动态切换
setInitMethodNamevoid setInitMethodName(String initMethodName)指定初始化方法名beanDefinition.setInitMethodName("init");配合 InitializingBean 或 @PostConstruct 使用
setDestroyMethodNamevoid setDestroyMethodName(String destroyMethodName)指定销毁方法名beanDefinition.setDestroyMethodName("cleanup");仅单例 Bean 有效,容器关闭时调用
afterPropertiesSetvoid afterPropertiesSet() throws Exception实现 InitializingBean 接口的初始化回调public class MyBean implements InitializingBean { public void afterPropertiesSet() { ... } }不推荐使用,建议用 @PostConstruct,避免耦合 Spring API
destroyvoid destroy() throws Exception实现 DisposableBean 接口的销毁回调public class MyBean implements DisposableBean { public void destroy() { ... } }同上,建议用 @PreDestroy 替代

说明:

  • BeanFactory 是 Spring 的底层容器接口,提供基本的依赖查找能力。
  • ApplicationContext 是 BeanFactory 的子接口,提供更多企业级功能(如事件发布、国际化、资源加载等),是实际开发中推荐使用的容器。
  • Bean 的作用域包括:singleton(默认)、prototyperequestsessionapplicationwebsocket
  • 生命周期回调建议使用 JSR-250 注解 @PostConstruct@PreDestroy,而非实现 Spring 特定接口,以降低耦合。

第二章:Spring 注解驱动开发

2.1 常用注解概述(@Component, @Service, @Repository, @Controller)

注解语法位置用途代码示例注意事项
@Component类级别通用组件注解,标识一个 Spring 管理的 Bean@Component public class EmailService { }最基础的注解,其他如 @Service 是其衍生
@Service类级别标识业务逻辑层组件@Service public class UserService { }语义化更强,便于 AOP 切入业务层
@Repository类级别标识数据访问层(DAO)组件@Repository public class UserRepository { }能自动翻译数据库异常为 Spring 的 DataAccessException
@Controller类级别标识控制层组件(MVC)@Controller public class UserController { }通常与 @RequestMapping 配合使用
@RestController类级别组合注解(@Controller + @ResponseBody)@RestController public class ApiUserController { }用于构建 RESTful API,返回数据而非视图

2.2 @Autowired 与 @Qualifier 依赖注入

注解语法位置用途代码示例注意事项
@Autowired字段、构造器、方法、参数自动装配 Bean,基于类型匹配@Autowired private UserService userService;可用于字段(不推荐)、构造器(推荐)、setter 方法
@Qualifier字段、参数、类型指定具体 Bean 名称,解决类型冲突@Autowired @Qualifier("premiumUserServiceImpl") private UserService userService;必须与 @Autowired 配合使用,用于多实现类场景
@Primary类级别标识首选 Bean,当存在多个候选时优先注入@Component @Primary public class DefaultPaymentService { }避免 @Qualifier 的重复使用,提高可读性
@Lookup方法支持方法注入,返回一个 Bean 实例@Lookup public Command createCommand() { return null; }用于单例 Bean 注入原型 Bean 的特殊场景
@Autowired(required=false)字段、方法允许依赖项为可选@Autowired(required = false) private OptionalService optionalService;若未找到匹配 Bean,不会报错,注入 null

2.3 @Value 注入基本类型与配置

注解语法位置用途代码示例注意事项
@Value字段、方法、参数注入属性值,支持字面量、SpEL、配置文件@Value("${app.name}") private String appName;支持 ${} 读取 properties,#{} 使用 SpEL 表达式
@Value (SpEL)字段使用 Spring 表达式语言注入动态值@Value("#{systemProperties['user.home']}") private String homeDir;可调用方法、访问系统属性、运算等
@Value (默认值)字段提供默认值@Value("${server.port:8080}") private int port;当配置项不存在时使用冒号后的默认值
@Value (数组/集合)字段注入集合类型@Value("#{'${db.urls}'.split(',')}") private List<String> urls;配合 SpEL 可实现字符串分割为集合
@Value (Resource)字段注入资源文件@Value("classpath:data.sql") private Resource dataScript;支持 classpath:、file:、http: 等资源协议

2.4 @Configuration 与 @Bean 配置类

注解/方法语法位置用途代码示例注意事项
@Configuration类级别标识配置类,等价于 XML 配置文件@Configuration public class AppConfig { }通常与 @Bean 方法配合使用,被 Spring 容器管理
@Bean方法级别定义一个 Bean,方法返回值注册为容器实例@Bean public DataSource dataSource() { return new DriverManagerDataSource(); }方法名默认为 Bean 名称,可自定义
@Bean (自定义名称)方法级别指定 Bean 的名称@Bean("myDataSource") public DataSource dataSource() { ... }当需要多个同类型 Bean 时用于区分
@Bean (初始化方法)方法级别指定初始化回调方法@Bean(initMethod = "init") public RedisClient redis() { ... }等价于 XML 中的 init-method 属性
@Bean (销毁方法)方法级别指定销毁回调方法@Bean(destroyMethod = "shutdown") public ThreadPool taskExecutor() { ... }容器关闭时调用,用于资源释放
@Import类级别导入其他配置类@Configuration @Import(DatabaseConfig.class) public class AppConfig { }实现配置类的模块化与复用
@ImportResource类级别导入 XML 配置文件@Configuration @ImportResource("classpath:beans.xml") public class XmlConfig { }用于兼容老项目或特定场景

2.5 @Scope、@Primary、@Lazy 注解使用

注解语法位置用途代码示例注意事项
@Scope类、方法(@Bean)指定 Bean 的作用域@Scope("prototype") @Component public class Task { }常用值:singleton、prototype、request、session
@Scope (代理模式)类、方法解决作用域代理问题@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)在单例中注入非单例 Bean 时需使用代理
@Primary类、方法(@Bean)标识首选 Bean@Bean @Primary public CacheManager ehCache() { ... }当存在多个候选 Bean 时优先注入
@Lazy类、方法、参数延迟初始化 Bean@Lazy @Component public class HeavyService { }减少启动时间,首次使用时才创建实例
@Lazy (条件延迟)构造器参数延迟注入依赖@Autowired public UserController(@Lazy UserService userService) { this.userService = userService; }构造器中使用 @Lazy 可延迟依赖的创建
@DependsOn类、方法强制指定初始化顺序@DependsOn("dataSource") @Bean public DataInitializer initializer() { ... }保证被依赖的 Bean 先初始化,避免空指针

第三章:Spring AOP(面向切面编程)

3.1 AOP 核心概念(切面、连接点、通知、切入点)

概念说明示例注意事项
切面(Aspect)横切关注点的模块化封装,通常是一个类日志记录、事务管理、安全控制使用 @Aspect 注解标识切面类
连接点(Join Point)程序执行过程中的特定点,如方法调用或异常抛出UserService 的 save() 方法调用前Spring AOP 仅支持方法级别的连接点
通知(Advice)切面在特定连接点上执行的动作在方法执行前打印日志分为前置、后置、环绕等类型
切入点(Pointcut)匹配连接点的表达式,定义通知在何处执行execution(* com.service.*.*(..))决定哪些类或方法被织入切面逻辑
引入(Introduction)为类添加新的方法或属性为目标类引入新的接口实现高级功能,较少使用
织入(Weaving)将切面应用到目标对象并创建代理对象的过程编译期、类加载期或运行期织入Spring AOP 在运行期通过代理实现

3.2 基于注解的 AOP 实现(@Aspect, @Before, @After 等)

注解语法位置用途代码示例注意事项
@Aspect类级别标识一个切面类@Component @Aspect public class LoggingAspect { }必须配合 @Component 或配置注册为 Bean
@Before方法级别前置通知,在目标方法执行前运行@Before("execution(* com.service.*.*(..))") public void logBefore(JoinPoint jp) { }无法阻止方法执行,除非抛出异常
@After方法级别后置通知,目标方法执行后运行(无论是否异常)@After("pointcut()") public void logAfter(JoinPoint jp) { }类似 finally 块,常用于资源清理
@AfterReturning方法级别返回通知,目标方法成功执行后运行@AfterReturning(pointcut="savePointcut()", returning="result") public void logReturn(Object result) { }可获取方法返回值,用于日志或缓存
@AfterThrowing方法级别异常通知,目标方法抛出异常后运行@AfterThrowing(pointcut="serviceLayer()", throwing="ex") public void logException(Exception ex) { }可获取异常对象,用于统一错误处理
@Around方法级别环绕通知,包裹目标方法,可控制执行流程@Around("execution(* .*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable { return pjp.proceed(); }最强大也最复杂,需显式调用 proceed()

3.3 切入点表达式语法详解

表达式类型语法格式用途代码示例注意事项
executionexecution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)匹配方法执行连接点execution(* com.service.UserService.save*(..))最常用,支持通配符:* 匹配任意字符,.. 匹配任意参数
withinwithin(type-pattern)匹配指定类型内的方法执行within(com.service.*)匹配 service 包下所有类的方法
thisthis(type)匹配代理对象类型为给定类型的 Beanthis(com.service.UserService)基于代理对象类型匹配,适用于 JDK 动态代理
targettarget(type)匹配目标对象类型为给定类型的 Beantarget(com.dao.UserRepository)基于目标对象类型匹配,更贴近实际业务对象
argsargs(arg-pattern)匹配参数类型符合指定模式的方法args(java.lang.String, ..)匹配第一个参数为 String 的方法,可结合类型安全使用
@target@target(annotation-type)匹配带有指定注解的类@target(org.springframework.stereotype.Service)匹配所有被 @Service 标注的类的方法
@within@within(annotation-type)匹配带有指定注解的类(与 @target 类似)@within(com.annotation.Loggable)常用于自定义注解切面
@annotation@annotation(annotation-type)匹配方法上带有指定注解的方法@annotation(com.annotation.Timer)适用于方法级自定义注解,如性能监控
@args@args(annotation-type)匹配参数上带有指定注解的方法@args(com.annotation.Validated)用于校验等场景
beanbean(id-or-name-wildcards)匹配指定 Bean 名称的方法bean(*Service)Spring 特有,支持通配符匹配 Bean 名称

3.4 环绕通知与异常通知处理

方法/注解语法用途代码示例注意事项
proceed()Object proceed() throws Throwable执行目标方法(ProceedingJoinPoint)try { return pjp.proceed(); } catch (Exception e) { ... }必须调用,否则目标方法不会执行
proceed(Object[])Object proceed(Object[] args) throws Throwable使用新参数执行目标方法Object[] newArgs = {"newName"}; return pjp.proceed(newArgs);可实现参数修改,如统一处理空值
throwing 属性@AfterThrowing(throwing="ex", ...)捕获异常对象@AfterThrowing(pointcut="serviceLayer()", throwing="ex") public void handle(Exception ex) { }参数名需与 throwing 属性一致
异常类型过滤@AfterThrowing(pointcut="...", throwing="ex") public void handle(IOException ex) { }指定捕获特定异常类型可分别处理不同异常,如 IOException、IllegalArgumentException支持多异常通知
环绕中捕获异常try { return pjp.proceed(); } catch (Exception e) { log.error(e); throw e; }在环绕通知中统一处理异常常用于记录异常日志、包装异常或返回默认值注意重新抛出异常以保持原有行为
异常转换catch (DataAccessException dae) { throw new ServiceException(dae); }将技术异常转换为业务异常在 DAO 层切面中常用,屏蔽底层实现细节提高上层调用的稳定性

3.5 AOP 底层实现原理(JDK 动态代理与 CGLIB)

机制触发条件实现原理代码/配置示例注意事项
JDK 动态代理目标类实现至少一个接口基于 java.lang.reflect.Proxy 为接口创建代理实例代理对象必须通过接口类型引用,如 UserService proxy = (UserService) Proxy.newProxyInstance(...)仅能代理接口方法,无法代理类的非接口方法
CGLIB 代理目标类未实现接口或强制使用基于 ASM 字节码生成库,创建目标类的子类并重写方法使用 @EnableAspectJAutoProxy(proxyTargetClass = true) 强制使用 CGLIB需添加 spring-core 中的 CGLIB 依赖;目标类不能为 final
proxyTargetClass@EnableAspectJAutoProxy(proxyTargetClass = true)强制使用 CGLIB 代理@Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) public class AopConfig { }默认为 false,优先使用 JDK 动态代理
exposeProxy@EnableAspectJAutoProxy(exposeProxy = true)暴露代理对象,解决 self-invocation 问题AopContext.currentProxy() 获取当前代理对象自调用(内部方法调用)无法被切面拦截,需启用此选项
自调用问题this.method()类内部方法调用绕过代理使用 ((UserService) AopContext.currentProxy()).save() 显式通过代理调用启用 exposeProxy 后方可使用 AopContext
代理对象类型判断instanceof 接口或类判断代理类型if (bean instanceof Advised) { ... }可通过 Spring 的 Advised 接口访问代理配置
Final 方法/类private、final 方法或类无法被代理final void doInternal() { } // 无法被 AOP 拦截CGLIB 也无法重写 final 方法,设计时应避免
性能对比JDK 代理轻量,CGLIB 生成子类开销略高大多数场景性能差异可忽略优先使用接口编程,便于切换代理方式

第四章:Spring 事务管理

4.1 事务的基本概念(ACID 特性)

特性全称说明Spring 中的体现注意事项
原子性(Atomicity)Atomicity事务是最小执行单位,不可分割,要么全部成功,要么全部失败@Transactional 方法中所有操作作为一个整体提交或回滚若方法中部分 SQL 执行成功,但后续出错,整个事务将回滚
一致性(Consistency)Consistency事务执行前后,数据库从一个一致状态转移到另一个一致状态通过业务逻辑和约束(如外键、唯一索引)保证数据正确性需开发者编写正确逻辑,Spring 不自动保证业务一致性
隔离性(Isolation)Isolation多个事务并发执行时,一个事务的操作不能被其他事务干扰通过 @Transactional(isolation = Isolation.READ_COMMITTED) 设置隔离级别隔离级别越高,并发性能越低,需权衡选择
持久性(Durability)Durability事务一旦提交,其结果是永久性的,即使系统故障也不会丢失提交后数据写入磁盘,由数据库保证依赖数据库的持久化机制,Spring 不直接管理
脏读(Dirty Read)一个事务读取了另一个未提交事务的数据设置 Isolation.READ_COMMITTED 可避免在 READ_UNCOMMITTED 级别下可能发生
不可重复读(Non-repeatable Read)同一事务内两次读取同一数据结果不同(因其他事务修改并提交)Isolation.REPEATABLE_READ 可避免比脏读更严重,影响事务内部一致性
幻读(Phantom Read)同一事务内两次查询范围数据,结果集不一致(因其他事务插入)Isolation.SERIALIZABLE 可避免通常通过行锁或表锁解决,影响并发

4.2 声明式事务管理(@Transactional 注解)

属性语法用途代码示例注意事项
value / transactionManagerString指定事务管理器 Bean 名称@Transactional("txManager")多数据源时必须指定,否则使用默认事务管理器
propagationPropagation 枚举设置事务传播行为@Transactional(propagation = Propagation.REQUIRED)默认为 REQUIRED,决定事务如何参与现有事务
isolationIsolation 枚举设置事务隔离级别@Transactional(isolation = Isolation.READ_COMMITTED)默认由数据库决定,可显式覆盖
timeoutint(秒)事务超时时间@Transactional(timeout = 30)超时后自动回滚,防止长时间占用资源
readOnlyboolean是否为只读事务@Transactional(readOnly = true)可提升性能,适用于查询操作,数据库可优化
rollbackForClass<? extends Throwable>[]指定哪些异常触发回滚@Transactional(rollbackFor = Exception.class)默认仅对 RuntimeException 和 Error 回滚
noRollbackForClass<? extends Throwable>[]指定哪些异常不触发回滚@Transactional(noRollbackFor = BusinessException.class)用于业务异常无需回滚的场景
labelString[]事务标签,用于监控或自定义逻辑@Transactional(label = "performance")Spring 5.2+ 支持,可用于 AOP 增强

4.3 事务传播行为与隔离级别

传播行为说明使用场景代码示例注意事项
REQUIRED支持当前事务,若无则新建大多数业务方法的默认选择@Transactional(propagation = Propagation.REQUIRED)外部有事务则加入,无则创建新事务
REQUIRES_NEW总是新建事务,挂起当前事务日志记录、独立扣款等需独立提交的场景@Transactional(propagation = Propagation.REQUIRES_NEW)内部事务提交/回滚不影响外部,常用于补偿机制
SUPPORTS支持当前事务,若无则以非事务方式执行查询方法,可有可无事务@Transactional(propagation = Propagation.SUPPORTS)不会主动开启事务,适合只读操作
NOT_SUPPORTED以非事务方式执行,挂起当前事务执行非事务性操作(如发送邮件)@Transactional(propagation = Propagation.NOT_SUPPORTED)避免事务占用过长时间
MANDATORY必须在已有事务中执行,否则抛异常强制要求调用方提供事务@Transactional(propagation = Propagation.MANDATORY)用于核心业务逻辑,确保一致性
NEVER以非事务方式执行,若当前有事务则抛异常确保不在事务中执行的操作@Transactional(propagation = Propagation.NEVER)防止误在事务中调用
NESTED嵌套事务,使用 Savepoint 实现需要部分回滚的场景@Transactional(propagation = Propagation.NESTED)JDK 1.4+ 支持,依赖数据库的 Savepoint 机制

4.4 事务的回滚机制与异常处理

方法/属性语法用途代码示例注意事项
rollbackFor@Transactional(rollbackFor = Exception.class)指定检查型异常也触发回滚@Transactional(rollbackFor = IOException.class) public void processFile() { ... }默认仅 RuntimeException 和 Error 回滚,需显式配置检查型异常
noRollbackFor@Transactional(noRollbackFor = BusinessException.class)指定某些异常不回滚@Transactional(noRollbackFor = ValidationException.class) public void createUser(User user) { ... }用于业务校验失败但无需回滚数据库的场景
手动回滚TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()在方法内手动标记回滚try { businessLogic(); } catch (ExternalException e) { TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); }适用于捕获异常后仍想回滚的复杂逻辑
异常抛出throw new RuntimeException("error")抛出运行时异常自动触发回滚if (user == null) { throw new IllegalArgumentException("User is null"); }是最常见、最推荐的回滚方式
try-catch 吞异常try { riskyOperation(); } catch (Exception e) { log.error(e); }捕获异常但不抛出或标记回滚若未重新抛出或调用 setRollbackOnly,则事务仍会提交危险操作:会导致数据不一致
嵌套事务回滚内层异常未被捕获内层事务异常传递到外层外层事务感知异常并整体回滚REQUIRED 传播下,异常会向上传播导致整个事务回滚
REQUIRES_NEW 回滚内部方法抛异常独立事务失败不影响外部外部可捕获内部异常并继续执行内部事务回滚,外部可选择是否回滚

4.5 编程式事务管理(TransactionTemplate)

方法语法用途代码示例注意事项
execute<T> T execute(TransactionCallback<T> action)执行事务性操作,支持返回值transactionTemplate.execute(status -> { userRepository.save(user); return userService.generateId(); });推荐使用,自动处理提交与回滚
executeWithoutResultvoid executeWithoutResult(Consumer<TransactionStatus> action)执行无返回值的事务操作(Spring 5.2+)transactionTemplate.executeWithoutResult(status -> { jdbcTemplate.update(sql, args); });简化无返回值场景的代码
setPropagationBehaviortransactionTemplate.setPropagationBehavior(Propagation.REQUIRES_NEW.value())设置事务传播行为TransactionTemplate tt = new TransactionTemplate(transactionManager); tt.setPropagationBehavior(Propagation.REQUIRES_NEW.value());可动态调整事务行为
setIsolationLeveltransactionTemplate.setIsolationLevel(Isolation.READ_UNCOMMITTED.value())设置隔离级别tt.setIsolationLevel(Isolation.SERIALIZABLE.value());根据业务需求设置,影响并发性能
setTimeouttransactionTemplate.setTimeout(30)设置事务超时时间(秒)tt.setTimeout(60);防止长时间事务占用连接
setReadOnlytransactionTemplate.setReadOnly(true)设置为只读事务tt.setReadOnly(true);提升查询性能,数据库可优化执行计划
setTransactionManagertransactionTemplate.setTransactionManager(txManager)指定事务管理器必须先设置事务管理器才能使用通常通过构造函数或依赖注入设置

说明:

  • 声明式事务(@Transactional)适用于大多数场景,代码简洁,关注点分离。
  • 编程式事务(TransactionTemplate)适用于复杂事务逻辑,如条件提交、多阶段操作等,灵活性更高。
  • 两者可结合使用,但应避免过度嵌套导致逻辑混乱。
  • 无论哪种方式,都需确保事务边界清晰,避免过长事务影响系统性能。

第五章:Spring 与数据访问(JDBC 与 ORM 集成)

5.1 Spring JDBC 模板(JdbcTemplate)

方法语法用途代码示例注意事项
queryForObject<T> T queryForObject(String sql, Class<T> requiredType)执行查询并返回单个对象(如 count、sum)Integer count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users", Integer.class);结果必须为单行单列,否则抛异常
queryForObject (带参数)<T> T queryForObject(String sql, Object[] args, Class<T> requiredType)带参数的单值查询String name = jdbcTemplate.queryForObject("SELECT name FROM users WHERE id = ?", new Object[]{1}, String.class);使用 ? 占位符,防止 SQL 注入
query<T> List<T> query(String sql, RowMapper<T> rowMapper)执行查询并返回对象列表List<User> users = jdbcTemplate.query("SELECT * FROM users", new UserRowMapper());需提供 RowMapper 实现结果集映射
query (BeanPropertyRowMapper)<T> List<T> query(String sql, BeanPropertyRowMapper<T> mapper)使用属性名自动映射List<User> users = jdbcTemplate.query("SELECT * FROM users", new BeanPropertyRowMapper<>(User.class));要求数据库列名与 Java 属性名匹配(支持驼峰转下划线)
updateint update(String sql, Object... args)执行 INSERT、UPDATE、DELETE 等更新操作int rows = jdbcTemplate.update("INSERT INTO users(name) VALUES(?)", "John");返回受影响的行数,可用于判断操作结果
batchUpdateint[] batchUpdate(String sql, List<Object[]> batchArgs)批量执行更新操作List<Object[]> batch = Arrays.asList(new Object[]{"Alice"}, new Object[]{"Bob"}); jdbcTemplate.batchUpdate("INSERT INTO users(name) VALUES(?)", batch);提升批量操作性能,减少网络往返
executevoid execute(String sql)执行任意 SQL(如 DDL)jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS temp (id INT)");用于执行建表、删表等管理语句
query (RowCallbackHandler)void query(String sql, RowCallbackHandler rch)逐行处理结果集(适用于大数据量)jdbcTemplate.query("SELECT * FROM large_table", rs -> { processRow(rs); });避免一次性加载全部数据到内存

5.2 数据源配置与连接池管理

方法/属性语法用途代码示例注意事项
DriverManagerDataSourcenew DriverManagerDataSource()简单数据源,不支持连接池DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName("com.mysql.cj.jdbc.Driver");仅用于测试,生产环境必须使用连接池
BasicDataSource (DBCP)org.apache.commons.dbcp2.BasicDataSourceDBCP2 连接池实现BasicDataSource ds = new BasicDataSource(); ds.setUrl("jdbc:mysql://localhost:3306/test"); ds.setUsername("root"); ds.setPassword("pass"); ds.setInitialSize(5); ds.setMaxTotal(20);需引入 commons-dbcp2 依赖,配置丰富
HikariDataSourcecom.zaxxer.hikari.HikariDataSourceHikariCP 高性能连接池HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); config.setUsername("root"); config.setPassword("pass"); config.setMaximumPoolSize(10); HikariDataSource ds = new HikariDataSource(config);Spring Boot 默认,性能优异,配置简洁
setInitialSizedataSource.setInitialSize(5)设置初始连接数生产环境建议与 minIdle 一致避免启动时连接创建开销
setMaxActive / setMaximumPoolSizedataSource.setMaxTotal(20)config.setMaximumPoolSize(20)设置最大连接数根据数据库承载能力和应用并发量调整过大会导致数据库压力过大
setMaxWait / setConnectionTimeoutdataSource.setMaxWaitMillis(5000)config.setConnectionTimeout(30000)获取连接的最长等待时间超时后抛出 SQLException建议设置合理超时,避免线程无限等待
setValidationQuerydataSource.setValidationQuery("SELECT 1")设置连接有效性检查 SQLHikariCP 默认自动检测,DBCP 需手动设置确保连接池返回有效连接
@Primary@Primary @Bean DataSource dataSource()指定主数据源(多数据源场景)当应用有多个 DataSource Bean 时,使用 @Primary 标识默认数据源避免 @Autowired 时出现歧义

5.3 集成 MyBatis 框架

方法/注解语法用途代码示例注意事项
@Mapper接口级别标识 MyBatis Mapper 接口@Mapper public interface UserMapper { User findById(Long id); }需配合 @MapperScan 使用,或每个接口添加
@MapperScan类级别(配置类)扫描指定包下的 Mapper 接口@Configuration @MapperScan("com.example.mapper") public class MyBatisConfig { }推荐方式,集中管理 Mapper 扫描
@Select方法级别定义查询 SQL@Select("SELECT * FROM users WHERE id = #{id}") User findById(Long id);支持动态 SQL,但复杂 SQL 建议写在 XML 中
@Insert方法级别定义插入 SQL@Insert("INSERT INTO users(name) VALUES(#{name})") @Options(useGeneratedKeys = true, keyProperty = "id") int insert(User user);@Options 用于获取自增主键
@Update方法级别定义更新 SQL@Update("UPDATE users SET name = #{name} WHERE id = #{id}") int update(User user);返回受影响行数
@Delete方法级别定义删除 SQL@Delete("DELETE FROM users WHERE id = #{id}") int delete(Long id);简单删除操作
@Param方法参数指定参数名称(多个参数时必需)User findByCondition(@Param("name") String name, @Param("age") int age);在 SQL 中使用 #{name}#{age} 引用
SqlSessionTemplatespring 提供的线程安全 SqlSession编程式访问数据库User user = sqlSession.selectOne("com.example.mapper.UserMapper.findById", 1L);可在非 Mapper 场景使用,灵活性高

5.4 集成 Hibernate 框架

方法/注解语法位置用途代码示例注意事项
@Entity类级别标识持久化实体类@Entity public class User { ... }必须有无参构造函数,类不能为 final
@Table类级别指定对应数据库表名@Entity @Table(name = "users") public class User { ... }name 属性可自定义表名,schema 属性指定模式
@Id字段/属性标识主键字段@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;必须存在,一个实体只能有一个 @Id
@GeneratedValue字段/属性定义主键生成策略@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")常用策略:IDENTITY、AUTO、SEQUENCE、TABLE
@Column字段/属性映射数据库列@Column(name = "user_name", nullable = false, length = 50) private String name;可指定列名、是否可空、长度、唯一性等
@ManyToOne字段/属性定义多对一关联@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dept_id") private Department department;fetch 默认 EAGER,建议按需设为 LAZY 防止 N+1 问题
@OneToMany字段/属性定义一对多关联@OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List<Order> orders;mappedBy 指向对方维护关系的字段,避免双向维护
@OneToOne字段/属性定义一对一关联@OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "profile_id") private Profile profile;可通过 @JoinColumn 或 @PrimaryKeyJoinColumn 指定关联方式
@ManyToMany字段/属性定义多对多关联@ManyToMany @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private Set<Role> roles;必须使用 @JoinTable 指定中间表
@Transactional (Hibernate)方法/类管理 Hibernate 会话与事务@Transactional public void saveUser(User user) { sessionFactory.getCurrentSession().save(user); }必须在事务中执行持久化操作

5.5 异常统一处理与模板回调

方法/机制语法用途代码示例注意事项
DataAccessExceptionSpring 数据访问异常根类统一异常体系,屏蔽底层 ORM 差异try { ... } catch (DataAccessException e) { log.error("DB error", e); }所有 Spring 数据访问操作抛出此体系异常,无需关心具体实现
SQLExceptionTranslator接口将 SQLException 转换为 Spring 的 DataAccessExceptionSQLExceptionTranslator translator = new SQLStateSQLExceptionTranslator(); DataAccessException ex = translator.translate("query", sql, sqlEx);JdbcTemplate 自动使用,开发者通常无需直接调用
RowMapper接口自定义结果集到对象的映射逻辑public class UserRowMapper implements RowMapper<User> { public User mapRow(ResultSet rs, int rowNum) throws SQLException { User u = new User(); u.setId(rs.getLong("id")); u.setName(rs.getString("name")); return u; } }适用于复杂映射或 BeanPropertyRowMapper 无法处理的场景
ResultSetExtractor接口提取整个结果集(如多行映射为集合)ResultSetExtractor<List<User>> extractor = rs -> { List<User> list = new ArrayList<>(); while (rs.next()) { list.add(mapRow(rs)); } return list; };比 RowMapper 更灵活,可处理复杂结果集结构
PreparedStatementSetter接口设置预编译语句参数PreparedStatementSetter pss = ps -> { ps.setString(1, name); ps.setInt(2, age); }; jdbcTemplate.update(sql, pss);用于复杂参数设置逻辑,替代简单的 Object[] 参数
BatchPreparedStatementSetter接口批量设置预编译语句参数BatchPreparedStatementSetter bpss = new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) { ps.setString(1, names.get(i)); } public int getBatchSize() { return names.size(); } };配合 batchUpdate 使用,实现动态批量操作
@ExceptionHandler (Data)方法(@ControllerAdvice)全局处理数据访问异常@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(DataAccessException.class) public ResponseEntity<String> handleDataError(DataAccessException e) { return ResponseEntity.status(500).body("Database error"); } }实现异常统一响应,提升 API 友好性

第六章:Spring MVC 框架详解

6.1 MVC 架构模式与 Spring MVC 核心组件

组件说明职责示例注意事项
MVC 模式Model-View-Controller 架构分离关注点,提高可维护性用户请求 → Controller 处理 → Model 数据 → View 展示适用于 Web 应用,实现前后端逻辑解耦
DispatcherServlet前端控制器(核心)接收所有请求,协调各组件工作DispatcherServlet 作为 Servlet 配置在 web.xml 或通过 Java 配置是 Spring MVC 的入口,所有请求的统一调度中心
HandlerMapping处理器映射根据请求 URL 找到对应的处理器(Controller 方法)@RequestMapping("/user") 映射到具体方法支持多种实现,如 RequestMappingHandlerMapping
HandlerAdapter处理器适配器调用处理器方法,适配不同类型的处理器调用 @Controller 中的 @RequestMapping 方法屏蔽处理器实现差异,实现统一调用
Controller控制器处理业务逻辑,返回模型和视图@Controller public class UserController { ... }接收请求参数,调用 Service,返回结果
ModelAndView模型与视图封装数据模型和视图信息return new ModelAndView("user/list", "users", userList);可同时返回数据和视图名
ViewResolver视图解析器将逻辑视图名解析为实际视图对象InternalResourceViewResolver 解析 JSP 路径支持 JSP、Thymeleaf、Freemarker 等多种模板
View视图渲染模型数据,生成响应内容JSP 页面、Thymeleaf 模板、JSON 数据最终呈现给用户的内容
Model模型业务数据的容器model.addAttribute("users", userList);用于在 Controller 和 View 之间传递数据

6.2 DispatcherServlet 配置与请求流程

步骤说明关键组件代码/配置示例注意事项
1. 请求到达客户端发送 HTTP 请求Web 容器(如 Tomcat)GET /user/list HTTP/1.1所有匹配 DispatcherServlet URL 模式的请求均被拦截
2. 前端控制器接收DispatcherServlet 接收请求DispatcherServletweb.xml 配置 <servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class></servlet>必须正确配置 <servlet-mapping>
3. 处理器映射查找根据 URL 查找处理器HandlerMapping@RequestMapping("/user/list")多个 HandlerMapping 按优先级匹配
4. 获取处理器适配器获取能执行该处理器的适配器HandlerAdapterSpring 自动选择合适的适配器开发者通常无需关心
5. 执行拦截器(preHandle)调用拦截器的前置处理方法HandlerInterceptorpreHandle(HttpServletRequest, HttpServletResponse, Object)可进行权限校验、日志记录等
6. 调用处理器方法执行 Controller 中的业务逻辑@Controller 方法public String list(Model model) { ... }参数自动绑定,支持多种返回类型
7. 处理返回值解析处理器返回值(视图名、Model 等)HandlerMethodReturnValueHandlerreturn "user/list";@ResponseBody返回值处理器决定如何处理结果
8. 执行拦截器(postHandle)调用拦截器的后置处理方法HandlerInterceptorpostHandle(HttpServletRequest, HttpServletResponse, Object, ModelAndView)可修改 ModelAndView
9. 视图解析将逻辑视图名解析为实际视图ViewResolverprefix="/WEB-INF/views/", suffix=".jsp"配置前缀和后缀简化视图名
10. 视图渲染使用模型数据渲染视图View(如 JSP)JSP 中使用 ${users} 访问模型数据生成 HTML、JSON 等响应内容
11. 执行拦截器(afterCompletion)请求完成后执行清理工作HandlerInterceptorafterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)无论成功或异常都会执行,适合资源释放

6.3 @Controller 与 @RequestMapping 映射

注解语法位置用途代码示例注意事项
@Controller类级别标识控制器类,交由 Spring 管理@Controller public class UserController { }需配合 <context:component-scan>@ComponentScan
@RestController类级别组合注解(@Controller + @ResponseBody)@RestController public class ApiUserController { }用于构建 RESTful API,返回 JSON/XML 数据
@RequestMapping类/方法映射请求 URL@RequestMapping("/user") 类 + @RequestMapping("/list") 方法类上定义基础路径,方法上定义具体路径
@RequestMapping (method)方法指定请求方法@RequestMapping(value = "/save", method = RequestMethod.POST)可限定 GET、POST、PUT、DELETE 等
@GetMapping方法简化 GET 请求映射@GetMapping("/list") public List<?> getAll() { ... }等价于 @RequestMapping(method = RequestMethod.GET)
@PostMapping方法简化 POST 请求映射@PostMapping("/save") public String saveUser(@RequestBody User user) { ... }常用于表单提交或 JSON 数据创建
@PutMapping方法简化 PUT 请求映射@PutMapping("/update/{id}") public ResponseEntity<?> update(@PathVariable Long id, @RequestBody User user) { ... }用于更新资源
@DeleteMapping方法简化 DELETE 请求映射@DeleteMapping("/delete/{id}") public String delete(@PathVariable Long id) { ... }用于删除资源
@RequestMapping (consumes)方法指定请求体内容类型@PostMapping(path = "/data", consumes = "application/json")限制只处理 JSON 请求
@RequestMapping (produces)方法指定响应内容类型@GetMapping(path = "/data", produces = "application/json;charset=UTF-8")支持内容协商,如 JSON、XML

6.4 请求参数绑定(@RequestParam, @PathVariable)

注解/方法语法位置用途代码示例注意事项
@RequestParam方法参数绑定请求参数(query string 或 form data)public String search(@RequestParam String keyword, @RequestParam(defaultValue = "1") int page) { ... }用于获取 ?keyword=spring&page=1 中的参数
@RequestParam (required)方法参数指定参数是否必需@RequestParam(required = false) String sort默认为 true,设为 false 可选,配合 defaultValue 使用
@RequestParam (defaultValue)方法参数设置参数默认值@RequestParam(defaultValue = "10") int size当参数缺失时使用默认值,避免空指针
@PathVariable方法参数绑定 URL 路径变量@GetMapping("/user/{id}") public User getUser(@PathVariable Long id) { ... }用于 RESTful 风格 URL,如 /user/123
@PathVariable (value)方法参数指定路径变量名称@GetMapping("/order/{orderId}") public Order getOrder(@PathVariable("orderId") Long id) { ... }当参数名与路径变量名不一致时使用
@MatrixVariable方法参数绑定 URL 矩阵变量(分号分隔)@GetMapping("/user/{id}") public User getUser(@MatrixVariable String city) { ... }URL 如 /user/123;city=beijing,较少使用
@RequestHeader方法参数绑定请求头信息@RequestHeader("User-Agent") String userAgent获取客户端信息、认证令牌等
@CookieValue方法参数绑定 Cookie 值@CookieValue("JSESSIONID") String sessionId读取客户端 Cookie
@RequestBody方法参数绑定请求体(JSON/XML)到对象@PostMapping("/user") public User createUser(@RequestBody User user) { ... }需配合 HttpMessageConverter(如 Jackson)
自动绑定(Command Object)方法参数将请求参数自动绑定到对象属性public String save(UserForm form) { ... }参数名需与对象属性名匹配,支持级联属性 address.city

6.5 数据绑定与类型转换

机制/注解说明用途代码示例注意事项
DataBinderSpring 数据绑定核心类将请求参数绑定到目标对象WebDataBinder binder = ...; binder.bind(propertyValues);自动处理类型转换和格式化
PropertyEditor旧式类型转换机制将字符串转换为特定类型public class CustomDateEditor extends PropertyEditorSupport { ... }已被 Converter 和 Formatter 取代,但仍兼容
Converter<S, T>通用类型转换接口实现自定义类型转换public class StringToUserConverter implements Converter<String, User>需注册到 ConversionService
Formatter格式化接口(支持 Locale)实现带格式的类型转换与反向格式化public class DateFormatter implements Formatter<Date>适用于日期、货币等需格式化的类型
@DateTimeFormat字段/参数指定日期时间格式@DateTimeFormat(pattern = "yyyy-MM-dd") private Date birthDate;用于表单提交的日期字符串转换
@NumberFormat字段/参数指定数字格式@NumberFormat(style = Style.CURRENCY) private BigDecimal salary;用于金额、百分比等格式化
ConversionService类型转换服务管理所有转换器@Autowired ConversionService conversionService;Spring MVC 自动配置,可通过 @EnableWebMvc 自定义
Validation (JSR-303)数据校验验证绑定后的对象public String save(@Valid User user, BindingResult result)需引入 hibernate-validator,配合 @NotNull、@Size 等使用
BindingResult方法参数接收数据绑定和校验结果if (result.hasErrors()) { ... }必须紧跟在 @Valid 参数后声明,用于处理错误

第七章:Spring Boot 快速开发

7.1 Spring Boot 简介与自动配置原理

概念说明实现机制代码/注解示例注意事项
Spring Boot 目标简化 Spring 应用的初始搭建和开发约定优于配置,内嵌容器,开箱即用spring-boot-starter-web 包含 Tomcat 和 Web MVC减少样板代码和 XML 配置
@SpringBootApplication组合注解(核心入口)组合了 @SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }主类应放在根包下,以便扫描所有组件
@EnableAutoConfiguration启用自动配置根据 classpath 中的依赖自动配置 Bean@EnableAutoConfiguration(通常由 @SpringBootApplication 包含)是自动配置的核心开关
spring.factories自动配置元数据文件META-INF/spring.factories 中定义自动配置类列表org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.example.autoconfig.UserAutoConfigurationSpring Boot 启动时加载并处理这些配置类
条件化配置 @Conditional基于条件决定是否创建 Bean多种衍生注解控制自动配置生效条件@ConditionalOnClass(DataSource.class) @ConditionalOnMissingBean(Service.class)常见条件:类路径存在、Bean 缺失、属性匹配等
自动配置类命名命名规范XXXAutoConfiguration 结尾DataSourceAutoConfiguration, WebMvcAutoConfiguration内部使用 @Configuration 定义一系列 Bean
Starter POMs场景化依赖集提供一键式依赖管理,避免版本冲突spring-boot-starter-data-jpa, spring-boot-starter-security引入 Starter 即可开始使用对应功能
内嵌服务器内置 Servlet 容器默认集成 Tomcat,也可切换为 Jetty 或 Undertow无需外部部署,直接运行 main 方法启动打包为 JAR 可独立运行

7.2 Starter 依赖与常用场景集成

Starter 名称功能Maven 依赖坐标典型用途注意事项
spring-boot-starter核心 Starterorg.springframework.boot:spring-boot-starter所有应用的基础依赖,包含自动配置、日志等每个 Spring Boot 项目都必须引入
spring-boot-starter-webWeb 开发spring-boot-starter-web构建 RESTful API 或传统 Web 应用,包含 Tomcat 和 Spring MVC创建 Web 应用的首选 Starter
spring-boot-starter-data-jpaJPA 数据访问spring-boot-starter-data-jpa集成 Hibernate,实现 ORM 操作需配合数据库驱动和连接池
spring-boot-starter-data-jdbcJDBC 数据访问spring-boot-starter-data-jdbc轻量级 JDBC 支持,基于 Spring JDBC不使用 ORM,追求性能和简单性
spring-boot-starter-data-redisRedis 集成spring-boot-starter-data-redis缓存、会话存储、消息队列自动配置 RedisTemplate 和 StringRedisTemplate
spring-boot-starter-security安全框架spring-boot-starter-security认证、授权、CSRF 防护引入后默认保护所有端点,需配置放行规则
spring-boot-starter-thymeleaf模板引擎spring-boot-starter-thymeleaf服务端页面渲染(HTML)适用于传统 MVC 应用,非前后端分离
spring-boot-starter-test测试支持spring-boot-starter-test单元测试和集成测试,包含 JUnit, Mockito, AssertJ测试范围依赖,不会打包到生产环境
spring-boot-starter-actuator应用监控spring-boot-starter-actuator暴露健康检查、指标、审计等端点生产环境需安全配置,避免信息泄露
spring-boot-starter-aopAOP 支持spring-boot-starter-aop启用 Spring AOP,自动引入 AspectJ用于日志、事务、权限等横切关注点

7.3 配置文件(application.properties/yml)管理

特性说明文件示例代码获取方式注意事项
application.properties属性文件格式server.port=8081 spring.datasource.url=jdbc:mysql://localhost:3306/test@Value("${server.port}") private int port;键值对形式,结构扁平,适合简单配置
application.ymlYAML 格式(推荐)server:\n port: 8081\nspring:\n datasource:\n url: jdbc:mysql://localhost:3306/test@Value("${spring.datasource.url}")@ConfigurationProperties层次清晰,支持复杂结构,注意缩进
配置优先级多种配置源,优先级不同命令行参数 > JVM 系统属性 > 配置文件 > 默认值java -jar app.jar --server.port=9090了解优先级有助于调试配置问题
@Value 注解注入单个配置值@Value("${app.name:MyApp}") private String appName;支持 SpEL 表达式和默认值(冒号后)适用于简单配置注入
@ConfigurationProperties类型安全的配置绑定@Component @ConfigurationProperties(prefix = "app") public class AppProperties { private String name; ... }需启用 @EnableConfigurationProperties 或组件扫描推荐方式,将相关配置组织在 POJO 中
配置文件位置外部化配置/config 子目录 > 当前目录 > classpath:/config/ > classpath:/--spring.config.location=file:/path/to/config/支持从多个位置加载配置
随机值生成生成随机属性secret.token=${random.value} user.id=${random.long}使用 RandomValuePropertySource用于密钥、ID 等需要随机性的场景
多文档块(YAML)在一个文件中定义多组配置spring:\n profiles: dev\n datasource:\n url: jdbc:h2:mem:dev\n---\nspring:\n profiles: prod\n datasource:\n url: jdbc:mysql://prod-db:3306/app结合 spring.profiles.active 使用方便管理不同环境的配置

7.4 日志配置与 Profile 环境切换

方法/配置说明配置示例代码/命令注意事项
默认日志系统Spring Boot 默认使用 Logback无需额外配置,自动集成import org.slf4j.Logger; import org.slf4j.LoggerFactory;基于 SLF4J 门面,实际实现是 Logback
logback-spring.xml自定义 Logback 配置<configuration><include resource="org/springframework/boot/logging/logback/defaults.xml"/><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">...<logger name="com.example" level="DEBUG"/></configuration>放置在 src/main/resources使用 -spring 后缀可利用 Spring 扩展功能(如 Profile)
application-{profile}.properties环境特定配置文件application-dev.properties, application-prod.propertieslogging.level.com.example=DEBUG文件中的配置会覆盖主配置文件
spring.profiles.active激活指定 Profilespring.profiles.active=dev,metrics命令行:--spring.profiles.active=prod可激活多个 Profile,逗号分隔
@Profile 注解条件化 Bean 注册@Profile("dev") @Bean public DataSource devDataSource() { ... }结合 @Configuration 使用Bean 只在指定 Profile 激活时创建
logging.level.*设置日志级别logging.level.root=WARN logging.level.com.example.service=DEBUG控制不同包的日志输出粒度生产环境建议设为 INFO 或 WARN
logging.file.name / logging.file.path指定日志文件logging.file.name=app.loglogging.file.path=./logs自动生成日志文件,支持滚动避免日志占满磁盘空间
Profile 分组将相关 Profile 分组spring.profiles.group.production[0]=proddb spring.profiles.group.production[1]=prodmq--spring.profiles.active=production简化复杂环境的激活操作

7.5 启动流程与 SpringApplication 自定义

阶段说明可自定义点示例代码注意事项
1. 创建 SpringApplication 实例初始化应用上下文SpringApplication app = new SpringApplication(MyApp.class);可设置资源加载器、Banner 等通常直接使用静态 run 方法
2. 运行 SpringApplicationrun(args) 方法启动SpringApplication.run(MyApp.class, args);返回 ConfigurableApplicationContext核心启动入口
3. 应用监听器调用发布启动事件实现 ApplicationListener<ApplicationStartingEvent>用于早期初始化或监控Spring Boot 提供多个生命周期事件
4. 推断应用类型判断是 Servlet、Reactive 还是其他自动判断 classpath无需手动干预影响后续容器的选择
5. 加载 ApplicationContextInitializer上下文初始化前回调app.addInitializers(ctx -> ctx.getBeanFactory().registerSingleton("myBean", new MyBean()));用于修改应用上下文
6. 加载 ApplicationListener加载事件监听器app.addListeners(new MyEventListener());响应 ContextRefreshedEvent 等事件
7. 推断主类确定主配置类通常为主类本身用于查找配置源
8. 环境构建创建并配置 Environmentapp.setEnvironment(new StandardEnvironment());可自定义环境变量来源
9. 配置 Environment 属性设置默认属性app.setDefaultProperties(Collections.singletonMap("server.port", "9090"));优先级低于外部配置
10. 打印 Banner输出启动 Logoapp.setBanner((environment, sourceClass, out) -> out.print("Welcome!"));可禁用:app.setBannerMode(Banner.Mode.OFF);
11. 创建 ApplicationContext根据应用类型创建上下文Servlet → AnnotationConfigServletWebServerApplicationContext核心容器创建
12. 失败分析仪异常时提供诊断信息FailureAnalyzers 自动注册DataSourceBeanCreationFailureAnalyzer提升错误可读性
13. 加载初始器调用 ApplicationContextInitializercontext.addBeanFactoryPostProcessor(...);修改 BeanFactory 配置
14. 环境关联将 Environment 关联到上下文context.setEnvironment(environment);完成环境注入
15. 上下文后处理Bean 定义加载前的最后处理applyInitializers(context);
16. 准备上下文刷新前准备(加载 Bean 定义)load(context, sources.toArray(new Object[0]));扫描 @Component 等注解
17. 刷新上下文核心步骤,实例化 Beancontext.refresh();触发 ContextRefreshedEvent
18. 调用 CommandLineRunner / ApplicationRunner应用启动后执行任务@Component public class StartupTask implements CommandLineRunner { public void run(String... args) { ... } }按 order 属性排序执行
19. 发布就绪事件应用启动完成context.publishEvent(new ApplicationReadyEvent(...));可监听此事件执行后续操作
20. 运行失败启动异常处理catch (Throwable ex) { handleRunFailure(context, ex, listeners); }输出详细错误信息

第八章:RESTful API 与 JSON 处理

8.1 REST 架构风格与设计规范

原则说明正确示例错误示例注意事项
资源导向以资源为中心设计 API/users, /orders/getUser, /createOrderURL 表示资源,而非操作
统一接口使用标准 HTTP 方法GET /users(查询), POST /users(创建), PUT /users/1(更新), DELETE /users/1(删除)POST /users/delete?id=1方法语义明确,符合幂等性
无状态每个请求包含所有必要信息服务端不保存会话状态,使用 Token 认证依赖服务器 Session 存储用户状态便于水平扩展
HATEOAS超媒体作为应用状态引擎响应中包含相关资源链接仅返回数据,客户端硬编码 URL提高 API 可发现性
版本控制API 版本管理/api/v1/users, Accept: application/vnd.myapp.v1+json/users-v1推荐 URL 路径或 Header 版本控制
HTTP 状态码正确使用状态码200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error所有成功返回 200,错误返回 200 带错误码客户端依赖状态码判断结果
资源命名使用名词、复数、小写/products, /user-profiles/getProducts, /Product避免动词和大小写混合
过滤与分页支持查询参数GET /users?status=active&page=1&size=10GET /users/active标准化分页参数(page, size)
安全传输使用 HTTPS所有生产环境 APIHTTP 明文传输保护数据安全
文档化提供 API 文档使用 OpenAPI (Swagger) 生成文档无文档或文档不更新工具:springdoc-openapi

8.2 @RestController 与 @RequestBody / @ResponseBody

注解语法位置用途代码示例注意事项
@RestController类级别组合注解(@Controller + @ResponseBody)@RestController public class UserController { @GetMapping("/users") public List<User> getAll() { ... } }所有方法返回值自动序列化为 JSON,无需 @ResponseBody
@ResponseBody方法/类将返回值写入 HTTP 响应体@Controller public class ApiController { @ResponseBody @GetMapping("/data") public Map<String, Object> getData() { ... } }通常与 @Controller 配合使用,返回 JSON 数据
@RequestBody方法参数将请求体 JSON 反序列化为对象@PostMapping("/users") public User createUser(@RequestBody User user) { ... }需配合 HttpMessageConverter(如 Jackson)
@ResponseStatus方法/类自定义 HTTP 响应状态码@PostMapping("/users") @ResponseStatus(HttpStatus.CREATED) public User createUser(@RequestBody User user) { ... }覆盖默认状态码(如 200)
ResponseEntity方法返回值精确控制响应(状态码、头、体)@GetMapping("/users/{id}") public ResponseEntity<User> getUser(@PathVariable Long id) { return userRepository.findById(id).map(user -> ResponseEntity.ok(user)).orElse(ResponseEntity.notFound().build()); }最灵活的方式,推荐用于复杂响应
@ExceptionHandler (API)方法(@ControllerAdvice)全局处理 API 异常并返回 JSON@ControllerAdvice public class ApiExceptionHandler { @ExceptionHandler(UserNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) @ResponseBody public ErrorResponse handleNotFound(UserNotFoundException e) { return new ErrorResponse("USER_NOT_FOUND", e.getMessage()); } }实现统一的错误响应格式

8.3 使用 Jackson 进行 JSON 序列化与反序列化

注解用途代码示例注意事项
@JsonProperty重命名 JSON 字段@JsonProperty("user_name") private String name;序列化/反序列化时使用指定名称
@JsonIgnore忽略字段@JsonIgnore private String password;敏感信息不暴露,也可用 transient
@JsonIgnoreProperties忽略多个字段或未知属性@JsonIgnoreProperties({"password", "temp"}) public class User { ... }@JsonIgnoreProperties(ignoreUnknown = true)防止反序列化时因未知字段报错
@JsonInclude控制 null/empty 值输出@JsonInclude(JsonInclude.Include.NON_NULL)减少 JSON 体积,避免 null 值
@JsonFormat日期格式化@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime;统一日期时间格式
@JsonPropertyOrder指定 JSON 字段顺序@JsonPropertyOrder({"id", "name", "email"})控制输出顺序,提高可读性
@JsonCreator自定义反序列化构造器@JsonCreator public User(@JsonProperty("name") String name) { ... }用于不可变对象或复杂构造
@JacksonInject注入值@JacksonInject private Service service;从外部注入依赖,非常规用法
@JsonRawValue原生 JSON 值@JsonRawValue private String jsonContent;字段值作为原始 JSON 插入
@JsonSerialize / @JsonDeserialize自定义序列化/反序列化器@JsonSerialize(using = CustomDateSerializer.class)实现复杂逻辑,如加密、特殊格式

配置示例:在 application.yml 中配置 Jackson 全局行为:

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
    serialization:
      write-dates-as-timestamps: false
    deserialization:
      fail-on-unknown-properties: false

8.4 统一响应格式设计

字段类型说明示例值注意事项
codeint业务状态码200, 400, 500区分于 HTTP 状态码,用于业务逻辑
messageString状态描述”Success”, “Invalid request”友好提示,可国际化
dataObject业务数据{ "id": 1, "name": "John" } 或 null实际返回的数据内容
timestamplong响应时间戳1715692800000便于问题追踪
successboolean是否成功true, false快速判断请求结果

统一响应类设计

public class ApiResponse<T> {
    private int code;
    private String message;
    private T data;
    private long timestamp;

    // 构造函数
    public ApiResponse(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
        this.timestamp = System.currentTimeMillis();
    }

    // 静态工厂方法
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(200, "Success", data);
    }

    public static <T> ApiResponse<T> error(int code, String message) {
        return new ApiResponse<>(code, message, null);
    }

    // getters and setters...
}

控制器使用示例

@RestController
public class UserController {
    @GetMapping("/users/{id}")
    public ApiResponse<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        return user != null ?
            ApiResponse.success(user) :
            ApiResponse.error(404, "User not found");
    }
}

8.5 文件上传与下载处理

场景方法/注解代码示例注意事项
单文件上传@RequestParam("file") MultipartFile file@PostMapping("/upload") public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return ResponseEntity.badRequest().body("File is empty"); } // 保存文件... return ResponseEntity.ok("Upload success"); }需配置 multipart.max-file-size 等限制
多文件上传@RequestParam("files") MultipartFile[] filespublic String uploadFiles(@RequestParam("files") MultipartFile[] files) { ... }循环处理每个文件
文件下载HttpServletResponse 输出流@GetMapping("/download/{filename}") public void downloadFile(@PathVariable String filename, HttpServletResponse response) throws IOException { File file = new File(uploadDir, filename); response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=" + filename); Files.copy(file.toPath(), response.getOutputStream()); }设置正确的 Content-Type 和 Content-Disposition
返回 ResourceResource 对象@GetMapping("/download/{filename}") public ResponseEntity<Resource> downloadFile(@PathVariable String filename) throws IOException { Path path = Paths.get(uploadDir).resolve(filename); Resource resource = new UrlResource(path.toUri()); return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"").body(resource); }Spring MVC 自动处理流,更安全
配置限制application.ymlspring:\n servlet:\n multipart:\n enabled: true\n max-file-size: 10MB\n max-request-size: 10MB防止过大文件导致服务器问题
大文件流式处理StreamingResponseBody@GetMapping("/large-file") public ResponseEntity<StreamingResponseBody> streamFile() { StreamingResponseBody stream = outputStream -> { outputStream.write(data); // 分块写入输出流 }; return ResponseEntity.ok().body(stream); }避免内存溢出,适用于超大文件

第九章:Spring Security 安全控制

9.1 认证(Authentication)与授权(Authorization)

概念说明核心组件示例注意事项
认证 (Authentication)验证”你是谁”AuthenticationManager, UserDetailsService, PasswordEncoder用户登录时验证用户名密码是授权的前提
授权 (Authorization)验证”你能做什么”AccessDecisionManager, SecurityExpressionOperations检查用户是否有权限访问 /admin基于角色或权限
Principal主体Authentication.getPrincipal()通常是 UserDetails 对象代表当前用户
Credentials凭证Authentication.getCredentials()密码(认证后通常设为 null)敏感信息
Authorities权限Authentication.getAuthorities()ROLE_ADMIN, PERM_USER_READ通常以 ROLE_ 开头表示角色
UserDetailsService用户信息加载loadUserByUsername(String username)从数据库加载用户信息必须实现此接口
PasswordEncoder密码编码器encode(), matches()使用 BCryptPasswordEncoder 加密密码绝对不要存储明文密码
SecurityContextHolder安全上下文持有者SecurityContextHolder.getContext().getAuthentication()获取当前用户信息ThreadLocal 存储,注意线程安全
Anonymous Authentication匿名认证AnonymousAuthenticationToken未登录用户被视为匿名用户可设置默认角色 ROLE_ANONYMOUS
Remember-Me记住我RememberMeServices登录时勾选”记住我”使用令牌长期有效

9.2 基于表单的登录配置

配置项说明代码示例注意事项
formLogin()启用表单登录http.formLogin();默认提供登录页 /login
自定义登录页指定登录页面http.formLogin().loginPage("/login").permitAll();登录页必须允许匿名访问
登录处理 URL处理登录请求.loginProcessingUrl("/doLogin")表单 action 应指向此 URL
成功处理登录成功后跳转.defaultSuccessUrl("/home", true).successHandler(...)true 表示总是跳转到指定页面
失败处理登录失败后跳转.failureUrl("/login?error").failureHandler(...)可在页面显示错误信息
用户名参数自定义用户名字段.usernameParameter("uname")表单 name 属性需匹配
密码参数自定义密码字段.passwordParameter("pword")
登出配置退出登录http.logout().logoutUrl("/logout").logoutSuccessUrl("/login");清除 Session 和 Remember-Me 令牌
Session 管理控制并发会话http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired");防止同一账号多处登录
CSRF 保护跨站请求伪造防护默认启用,表单需包含 _csrf对 REST API 可禁用 .csrf().disable()

9.3 方法级安全控制(@PreAuthorize, @Secured)

注解用途代码示例注意事项
@PreAuthorize方法执行前授权检查@PreAuthorize("hasRole('ADMIN')") public void deleteUser(Long id) { ... }支持 SpEL 表达式,功能最强大
@PostAuthorize方法执行后授权检查@PostAuthorize("returnObject.owner == authentication.name") public Document getDocument(Long id) { ... }基于返回值进行检查
@Secured基于角色的简单授权@Secured("ROLE_ADMIN") public void adminTask() { ... }仅支持角色检查,不支持 SpEL
@RolesAllowedJSR-250 注解@RolesAllowed("ADMIN") public void manage() { ... }标准注解,需启用 @EnableGlobalMethodSecurity(jsr250Enabled=true)
SpEL 表达式复杂权限逻辑@PreAuthorize("#userId == authentication.principal.id")# 引用方法参数,authentication 引用当前认证
启用方法安全开启注解支持@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class MethodSecurityConfig { }必须添加此配置类
结合参数动态权限检查@PreAuthorize("hasPermission(#id, 'document', 'read')") public Document read(Long id) { ... }需自定义 PermissionEvaluator
@DenyAll / @PermitAll拒绝/允许所有@DenyAll public void forbidden() { }明确的访问控制

9.4 CSRF 与 CORS 配置

配置项说明代码示例注意事项
CSRF 启用默认开启http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());保护表单提交
CSRF 禁用REST API 场景http.csrf().disable();仅在无状态 API 中使用,确保其他安全措施
CORS 配置跨域资源共享http.cors().configurationSource(corsConfigurationSource());处理浏览器跨域请求
全局 CORS全局配置@Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOriginPatterns(Arrays.asList("*")); config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); config.setAllowedHeaders(Arrays.asList("*")); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; }推荐方式,集中管理
@CrossOrigin类/方法级 CORS@CrossOrigin(origins = "https://example.com")适用于特定控制器或方法
CSRF Token 传递前端获取 Token从 Cookie XSRF-TOKEN 读取,放入请求头 X-XSRF-TOKENAngular 等框架自动处理
CORS 预检请求处理 OPTIONS服务器自动响应 Access-Control-Allow-*不需要手动处理
安全权衡CSRF vs CORS同时启用时需谨慎配置确保 withHttpOnlyFalse() 允许 JS 读取 CSRF Cookie

9.5 集成 JWT 实现无状态认证

组件说明代码/库注意事项
JWT 结构三部分:Header.Payload.SignatureeyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cBase64 编码,点分隔
jjwt 库JWT 实现io.jsonwebtoken:jjwt-api:0.11.5官方推荐库
Token 生成创建 JWTString token = Jwts.builder().setSubject(user.getUsername()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + 86400000)).signWith(SignatureAlgorithm.HS512, secretKey).compact();使用强密钥,设置合理过期时间
Token 解析验证并解析try { Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); String username = claims.getBody().getSubject(); } catch (JwtException e) { /* 处理异常 */ }捕获 SignatureException、ExpiredJwtException 等
AuthenticationFilter拦截请求继承 OncePerRequestFilter,从 Authorization 头提取 TokenBearer <token>
UserDetailsService加载用户通过解析出的用户名从数据库加载 UserDetails用于构建 Authentication 对象
SecurityContext设置认证SecurityContextHolder.getContext().setAuthentication(authentication);完成认证流程
刷新 Token长期有效机制额外发放 Refresh Token,用于获取新 Access Token提高安全性,避免频繁登录
Token 存储前端存储localStorage 或 HttpOnly CookieHttpOnly 更安全,防止 XSS
黑名单退出登录使用 Redis 存储失效 Token 的 JTI (JWT ID)实现主动退出和 Token 吊销

第十章:Spring 高级特性与扩展

10.1 事件驱动模型(ApplicationEvent 与 Listener)

组件说明代码示例注意事项
ApplicationEvent自定义事件public class UserRegisteredEvent extends ApplicationEvent { private final User user; public UserRegisteredEvent(User user) { super(user); this.user = user; } public User getUser() { return user; } }继承 ApplicationEvent,携带事件数据
ApplicationListener事件监听器@Component public class UserRegistrationListener implements ApplicationListener<UserRegisteredEvent> { @Override public void onApplicationEvent(UserRegisteredEvent event) { System.out.println("Welcome email sent to: " + event.getUser().getEmail()); } }实现接口,处理特定事件
@EventListener注解式监听@EventListener public void handleUserRegistration(UserRegisteredEvent event) { ... }更简洁,支持 SpEL 条件 @EventListener(condition = "#event.user.active")
ApplicationEventPublisher发布事件@Autowired private ApplicationEventPublisher publisher; publisher.publishEvent(new UserRegisteredEvent(user));在业务逻辑中发布事件
异步监听异步处理事件@EventListener @Async public void handleAsync(UserRegisteredEvent event) { ... }需启用 @EnableAsync,避免阻塞主流程
事务绑定事件事务提交后触发@EventListener @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)确保事件在事务成功后才处理
内置事件Spring 容器事件ContextRefreshedEvent, ContextStartedEvent, ContextClosedEvent监听容器生命周期
泛型事件类型安全事件public class GenericEvent<T> extends ApplicationEvent { ... }提高事件处理的灵活性

10.2 条件注解(@Conditional)与自动装配控制

注解用途代码示例注意事项
@Conditional条件化 Bean 注册@Conditional(OnClassCondition.class)核心条件注解,需指定条件类
@ConditionalOnClass类路径存在指定类@ConditionalOnClass(DataSource.class)常用于自动配置
@ConditionalOnMissingBean容器中不存在指定 Bean@ConditionalOnMissingBean(DataSource.class)避免重复定义,允许用户覆盖
@ConditionalOnProperty配置属性匹配@ConditionalOnProperty(name = "app.feature.enabled", havingValue = "true")基于 application.yml 配置启用功能
@ConditionalOnWebApplicationWeb 应用环境@ConditionalOnWebApplication区分 Web 和非 Web 环境
@ConditionalOnExpressionSpEL 表达式条件@ConditionalOnExpression("${app.cache.enabled} and ${app.redis.enabled}")复杂条件判断
@Profile环境 Profile 条件@Profile("dev")基于 spring.profiles.active
自定义条件实现复杂逻辑public class CustomCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return "custom".equals(context.getEnvironment().getProperty("app.mode")); } }实现 Condition 接口

10.3 国际化(i18n)支持

组件说明配置/代码注意事项
MessageSource消息源@Bean public MessageSource messageSource() { ... }Spring 提供 ReloadableResourceBundleMessageSource
资源文件语言包messages.properties, messages_zh_CN.properties, messages_en_US.properties文件名前缀相同,后缀为语言代码
配置 MessageSourceJava 配置@Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasename("classpath:messages"); messageSource.setDefaultEncoding("UTF-8"); return messageSource; }设置基名和编码
LocaleResolver区域解析器AcceptHeaderLocaleResolver, CookieLocaleResolver, SessionLocaleResolver决定当前请求的 Locale
LocaleChangeInterceptor区域变更拦截器@Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor(); interceptor.setParamName("lang"); return interceptor; }通过请求参数(如 ?lang=zh_CN)切换语言
Thymeleaf 使用模板中使用<p th:text="#{welcome.message}">Welcome</p>#{} 语法
Java 代码使用程序中获取@Autowired private MessageSource messageSource; String msg = messageSource.getMessage("error.required", null, Locale.CHINA);提供默认值和 Locale
日期/数字格式化格式化输出@DateTimeFormat, @NumberFormat结合 Locale 自动格式化

10.4 缓存抽象(@Cacheable, @CacheEvict)

注解用途代码示例注意事项
@Cacheable缓存方法结果@Cacheable(value = "users", key = "#id") public User getUser(Long id) { ... }方法执行前检查缓存,命中则返回缓存值
@CachePut更新缓存@CachePut(value = "users", key = "#user.id") public User updateUser(User user) { ... }方法总是执行,并将结果放入缓存
@CacheEvict清除缓存@CacheEvict(value = "users", key = "#id") public void deleteUser(Long id) { ... }清除指定缓存条目
@Caching组合多个缓存操作@Caching(evict = { @CacheEvict("users"), @CacheEvict(value = "cache2", key = "#user.id") })同时执行多个操作
CacheManager缓存管理器ConcurrentMapCacheManager, RedisCacheManager, EhCacheCacheManager需配置具体的缓存实现
启用缓存开启缓存支持@EnableCaching主配置类上添加
SpEL in Key动态缓存键key = "#user.name + '_' + #user.age"使用 SpEL 表达式生成缓存键
条件缓存条件化缓存@Cacheable(value = "users", condition = "#id < 100")满足条件才缓存
缓存未命中处理空值@Cacheable(..., cacheNullValues = false)避免缓存 null 值造成”缓存穿透”
TTL 配置设置过期时间Redis 中配置 timeToLive防止缓存数据长期不一致

10.5 异步任务执行(@Async)

注解/配置用途代码示例注意事项
@Async标记异步方法@Async public void sendEmail(String to, String content) { ... }方法将在单独线程中执行
@EnableAsync启用异步支持@Configuration @EnableAsync public class AsyncConfig { }必须添加以启用 @Async
返回值异步方法返回值public Future<String> processTask()public CompletableFuture<String>Future 用于获取结果,CompletableFuture 支持回调
自定义线程池配置线程池@Bean("taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix("async-"); executor.initialize(); return executor; }避免使用默认线程池,防止资源耗尽
指定执行器使用特定线程池@Async("taskExecutor")当有多个 Executor Bean 时指定
异常处理异步异常实现 AsyncUncaughtExceptionHandler@Async 方法的异常不会被主线程捕获
调用限制同类调用@Async 方法不能在同一个类中被直接调用@Async 是通过 Spring AOP 代理实现的,自调用会绕过代理

附录:Spring Boot 常用功能与进阶

附1 Spring Boot Actuator 监控端点

端点 (Endpoint)默认启用敏感性用途访问路径注意事项
/health显示应用健康状态(数据库、磁盘等)/actuator/health生产环境可暴露,支持 show-details=always
/info显示应用信息(git、构建等)/actuator/info需在 application.yml 中配置 info.* 属性
/metrics展示应用性能指标(JVM、HTTP、缓存等)/actuator/metrics可查看具体指标如 /actuator/metrics/jvm.memory.max
/beans列出容器中所有 Bean 及其依赖关系/actuator/beans暴露后可能泄露架构信息,生产慎用
/env显示所有环境变量和配置属性/actuator/env包含敏感信息(如密码),严禁生产暴露
/mappings列出所有 @RequestMapping 端点/actuator/mappings用于调试路由问题
/conditions显示自动配置的启用/禁用条件/actuator/conditions调试自动配置问题非常有用
/httptrace追踪最近的 HTTP 请求响应/actuator/httptrace需引入 spring-boot-starter-actuator
/shutdown关闭应用(POST 请求)/actuator/shutdown必须显式启用,生产环境极度危险
/prometheus暴露 Prometheus 格式的监控数据/actuator/prometheus需引入 micrometer-registry-prometheus

安全提示:通过 management.endpoints.web.exposure.include=* 可暴露所有端点,但生产环境必须结合 Spring Security 进行权限控制。

附2 Spring Boot 集成安全框架(Spring Security)

配置/注解用途代码示例注意事项
spring-boot-starter-security引入安全依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>引入后默认保护所有端点
WebSecurityConfigurerAdapter安全配置类(旧方式)@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/public/**").permitAll().anyRequest().authenticated().and().formLogin(); } }Spring Boot 2.7+ 推荐使用组件化配置
SecurityFilterChain新一代安全配置(推荐)@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(authz -> authz.requestMatchers("/public/**").permitAll().anyRequest().authenticated()).formLogin(withDefaults()); return http.build(); }基于 Lambda,代码更简洁
@EnableWebSecurity启用 Web 安全@Configuration @EnableWebSecurity public class SecurityConfig { ... }通常与 @Configuration 一起使用
内存用户存储测试用用户配置@Bean public UserDetailsService users() { UserDetails user = User.builder().username("user").password("{noop}password").roles("USER").build(); return new InMemoryUserDetailsManager(user); }{noop} 表示不加密,生产环境必须使用 BCrypt
JDBC 用户存储数据库存储用户http.authorizeRequests()....and().jdbcAuthentication().dataSource(dataSource);需要标准的 users、authorities 表结构
CSRF 防护防跨站请求伪造默认启用,表单需包含 _csrf token对 REST API 可禁用:.csrf().disable()
JWT 集成实现无状态认证需自定义 AuthenticationFilter 和 AuthorizationFilter结合 jjwt 库生成和验证 Token

附3 Spring Boot 测试(Test)

注解/类用途代码示例注意事项
@SpringBootTest集成测试主注解@SpringBootTest(classes = MyApp.class)加载完整上下文,启动服务器(可选)
@WebMvcTestWeb 层测试@WebMvcTest(UserController.class)仅加载 Web 相关 Bean,速度快
@DataJpaTestJPA 层测试@DataJpaTest配置内存数据库(如 H2),自动管理事务
@MockBean创建 Mock Bean@MockBean private UserService userService;替换上下文中真实的 Bean,用于隔离测试
TestRestTemplate测试 HTTP 请求@Autowired private TestRestTemplate restTemplate;发送请求到嵌入式服务器,无需端口管理
@BeforeEach / @AfterEachJUnit 5 生命周期@BeforeEach void setUp() { ... }替代 JUnit 4 的 @Before / @After
@Sql执行 SQL 脚本@Sql("/test-data.sql")在测试前或后初始化数据库
MockMvc模拟 MVC 请求@Autowired private MockMvc mockMvc;不启动服务器,直接调用控制器,速度极快
@TestPropertySource自定义测试属性@TestPropertySource(properties = "app.name=test")覆盖配置文件中的属性
Assertions (AssertJ)断言库assertThat(result).isNotNull().hasSize(2);比 JUnit 原生断言更强大、更易读

附4 Spring Boot 应用打包与部署

方法/工具说明命令/配置注意事项
Maven 打包使用 Maven 构建可执行 JARmvn clean package生成 target/*.jar,包含所有依赖
Gradle 打包使用 Gradle 构建gradle build生成 build/libs/*.jar
可执行 JAR内嵌容器的独立包java -jar myapp.jarSpring Boot 默认打包方式,无需外部 Tomcat
WAR 包部署部署到外部 Servlet 容器1. 修改 pom.xml <packaging>war</packaging> 2. 主类继承 SpringBootServletInitializer适用于必须使用外部容器的场景
Docker 镜像容器化部署FROM openjdk:17-jre COPY target/app.jar app.jar ENTRYPOINT ["java", "-jar", "/app.jar"]使用分层 JAR 或构建优化镜像大小
Profile 激活指定运行环境java -jar app.jar --spring.profiles.active=prod结合配置文件实现环境隔离
外部配置覆盖内嵌配置java -jar app.jar --server.port=9090命令行参数优先级最高
JVM 参数调优优化运行性能java -Xms512m -Xmx1024m -jar app.jar根据服务器资源设置堆大小
进程守护后台运行应用nohup java -jar app.jar > app.log 2>&1 &使用 systemd 或 supervisor 更佳
健康检查容器编排集成结合 /actuator/health 端点Kubernetes 中用于 Liveness/Readiness 探针

附5 Spring Boot 与微服务初步

概念说明实现方式注意事项
服务拆分将单体应用拆分为微服务每个服务独立开发、部署、数据库遵循单一职责原则,避免服务过大
RESTful API服务间通信使用 @RestController 暴露 JSON API设计清晰的 API 文档(如 OpenAPI)
Feign Client声明式 HTTP 客户端@FeignClient("user-service") public interface UserClient { @GetMapping("/users/{id}") User findById(@PathVariable Long id); }需引入 spring-cloud-starter-openfeign
Eureka 注册中心服务发现服务启动时注册,调用时从注册中心发现需搭建 Eureka Server,实现高可用
Ribbon 负载均衡客户端负载均衡Feign 默认集成 Ribbon支持轮询、随机等策略
Hystrix 断路器容错与降级防止雪崩效应,超时熔断已进入维护模式,推荐使用 Resilience4j
Zuul / GatewayAPI 网关统一入口、路由、过滤、限流Spring Cloud Gateway 基于 WebFlux,性能更优
Config Server集中化配置管理从 Git 仓库加载配置,支持动态刷新使用 @RefreshScope 刷新 Bean
Sleuth + Zipkin分布式链路追踪生成 Trace ID,追踪请求链路诊断跨服务调用问题
独立数据库数据库隔离每个微服务拥有独立数据库避免直接跨库访问,通过 API 通信