主题
Spring Framework
Spring 的核心是 IoC 容器:容器创建并装配 Bean,应用代码声明依赖而不自行查找或构造协作对象。Spring Boot 在此基础上提供自动配置、外部化配置和应用启动能力。
Bean、IoC 与配置方式
Bean 是由 Spring 容器创建、配置和管理的对象。现代项目优先使用 Java 配置和组件扫描;XML 仍适用于维护遗留系统或需要外部化装配定义的场景。
java
import java.time.Clock;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.example.app")
class AppConfig {
@Bean
Clock clock() {
return Clock.systemUTC();
}
}@Configuration 声明配置类,@Bean 声明第三方库或需要显式构造的 Bean。业务组件通常使用:
| 注解 | 适用对象 |
|---|---|
@Component | 通用组件 |
@Service | 业务服务 |
@Repository | 数据访问组件;可参与数据访问异常转换 |
@Controller / @RestController | Web 层控制器 |
这些注解的主要区别是语义表达;扫描与注册机制相同。
依赖注入
优先使用构造器注入:依赖不可变、可在单元测试中直接传入,并能尽早暴露循环依赖。只有一个构造器时不需要标记 @Autowired。
java
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentClient paymentClient;
public OrderService(OrderRepository orderRepository, PaymentClient paymentClient) {
this.orderRepository = orderRepository;
this.paymentClient = paymentClient;
}
}同一类型存在多个 Bean 时,用 @Qualifier 指定注入目标,或用 @Primary 指定默认候选者。配置值优先通过类型安全的 @ConfigurationProperties 绑定;不要在业务代码中散落读取环境变量。
java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@ConfigurationProperties(prefix = "app.mail")
public record MailProperties(String host, int port) {}
@Configuration
@EnableConfigurationProperties(MailProperties.class)
class PropertiesConfig {}作用域与生命周期
| 作用域 | 含义 |
|---|---|
singleton | 默认值;每个 Spring 容器一个实例 |
prototype | 每次获取 Bean 创建一个实例 |
request、session | Web 环境中按 HTTP 请求或会话创建 |
singleton 不等于 JVM 全局单例;它只在对应容器内唯一。容器创建 Bean 时依次完成实例化、依赖注入、初始化回调和后置处理。需要资源初始化与释放时可使用 @PostConstruct、@PreDestroy;对于 prototype Bean,销毁回调通常需要由获取方负责触发。
java
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
public class FileClient {
@PostConstruct
void connect() {}
@PreDestroy
void close() {}
}不要依赖字段注入或容器查找 API 来隐藏依赖;它们会增加测试和重构成本。
AOP
Spring AOP 通过代理在方法调用前后织入横切逻辑,适合日志、鉴权、指标、缓存和事务。核心概念:
| 概念 | 含义 |
|---|---|
| Aspect | 横切逻辑的模块,例如审计切面 |
| Pointcut | 匹配连接点的规则 |
| Advice | 在匹配处执行的逻辑,如 @Before、@Around |
| Proxy | Spring 创建的代理对象,负责拦截调用 |
java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TimingAspect {
@Around("execution(* com.example.app.service..*(..))")
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
long startedAt = System.nanoTime();
try {
return joinPoint.proceed();
} finally {
long elapsed = System.nanoTime() - startedAt;
// 记录耗时,不记录密码、令牌等敏感参数
}
}
}代理只拦截经过代理对象的调用。同一个 Bean 内部使用 this.method() 的自调用不会触发通常的 Spring AOP 通知;应重构职责边界,或改由另一个 Bean 调用。
声明式事务
@Transactional 通常标注在 Service 的公共业务方法上。事务边界应覆盖完成一个业务动作所需的所有数据库写入,而非单个 Repository 或 Mapper 调用。
java
import java.math.BigDecimal;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TransferService {
private final AccountRepository accountRepository;
public TransferService(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
@Transactional
public void transfer(long fromId, long toId, BigDecimal amount) {
accountRepository.debit(fromId, amount);
accountRepository.credit(toId, amount);
}
}默认情况下,未捕获的 RuntimeException 和 Error 会触发回滚;受检异常需要通过 rollbackFor 明确声明,或转换为合适的运行时异常。常用属性:
| 属性 | 用途 |
|---|---|
propagation | 定义嵌套调用如何参与现有事务 |
isolation | 设置隔离级别;按数据库与并发需求选择 |
readOnly | 声明只读意图,不能替代权限控制 |
timeout | 限制事务最长执行时间 |
rollbackFor | 扩展触发回滚的异常类型 |
事务同样基于代理,因此自调用、非代理管理对象和异步线程中的调用不会自动继承当前事务。捕获异常后若继续正常返回,事务可能提交;需要回滚时应重新抛出异常或显式标记回滚。
数据访问与测试
- 数据访问层只负责持久化;事务、编排和业务规则放在 Service 层。
- 使用连接池和
DataSource;不要在业务方法中手动创建数据库连接。 - 集成测试根据范围选择加载完整上下文或数据访问切片;单元测试直接构造被测对象并替换其依赖。
- 配置、密钥和运行环境差异通过外部化配置处理,密钥不进入源码或日志。
XML 的定位
XML Bean 定义、<aop:config>、<tx:advice> 仍受 Spring 支持,但新项目优先使用 Java 配置、@Aspect 与 @Transactional。迁移遗留系统时可逐步替换,避免在同一功能上混用两套配置来源。
复习要点
- IoC 容器管理 Bean;构造器注入优先。
singleton是容器级单例,prototype的销毁通常由使用方管理。- AOP 与事务依赖代理,自调用是常见失效点。
- 事务放在 Service 层,回滚规则和传播行为按业务语义显式设计。
