IoC 与 DI 思想
控制反转(IoC)把对象的创建与依赖组装交给容器;依赖注入(DI)是具体实现:对象不自己 new 依赖,而是由容器注入。降低耦合、便于替换与测试。
类只声明"我需要什么接口",由容器决定给哪个实现。换实现、做 mock 测试都无需改业务代码。
@SpringBootApplication
这个组合注解开启自动配置、组件扫描与配置类。启动类的 main 调 SpringApplication.run 拉起整个容器。
@SpringBootApplication // = @Configuration + @EnableAutoConfiguration + @ComponentScan
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}声明 Bean
用 @Component(及其特化 @Service/@Repository/@Controller)或 @Bean(在 @Configuration 里)把对象交给容器管理。
@Service
public class OrderService {
private final OrderRepository repo;
public OrderService(OrderRepository repo) { this.repo = repo; }
}
@Configuration
public class AppConfig {
@Bean
public Clock clock() { return Clock.systemUTC(); }
}Bean 作用域
默认单例(singleton,全程一个实例);还有 prototype(每次新实例)、request、session(Web 范围)。无状态服务用单例,有状态需谨慎。
| 作用域 | 生命周期 | 适用 |
|---|---|---|
| singleton | 容器级单例 | 无状态服务/DAO |
| prototype | 每次注入新建 | 有状态对象 |
| request | 每个 HTTP 请求 | Web 上下文 |
依赖注入方式
构造器注入(推荐,不可变、必填)、Setter 注入(可选)、字段注入(@Autowired,不推荐,难测试)。
@Service
public class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) { // 构造器注入
this.repo = repo;
}
}配合 final 字段保证依赖不可变、必填,且便于单元测试直接传 mock。
自动配置原理
Spring Boot 扫描 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,按条件(类路径、已有 Bean、配置开关)装配 Bean,免去大量 XML。
@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
public class DataSourceAutoConfiguration { /* 条件满足才生效 */ }配置外部化
配置写 application.yml,按 profile(dev/test/prod)覆盖;敏感信息用环境变量或配置中心,不进代码库。
spring:
datasource:
url: ${DB_URL:jdbc:h2:mem:testdb}
username: ${DB_USER:sa}
---
spring:
config:
activate:
on-profile: prod密码、token 走环境变量或 Vault/配置中心,CI 注入。提交到仓库是重大安全事故。
常见陷阱与 FAQ
单例构造器循环依赖 Spring 启动会报错。用 @Lazy 延迟、或重构把公共逻辑抽到第三方类打破环。更好的做法是重新设计依赖方向。
它绕过了构造器,导致类在无容器环境下难以实例化与测试,且隐藏了必填依赖。构造器注入更显式。
用 @Qualifier 指定名字,或让一个实现 @Primary。也能用 List<接口> 注入所有实现做策略选择。