Spring Core / IoC 容器
Spring IoC 容器是整个 Spring 生态的基础。理解 Bean 的生命周期和依赖注入原理,是掌握 Spring 的关键。
IoC 与 DI
IoC(控制反转):对象的创建和依赖关系的管理,从代码中转移到容器。
DI(依赖注入):容器在创建对象时,自动注入其依赖的其他对象。
java
// 传统方式:手动创建依赖
public class OrderService {
private UserRepository userRepo = new UserRepositoryImpl(); // 强耦合
private OrderRepository orderRepo = new OrderRepositoryImpl();
}
// IoC 方式:依赖由容器注入
@Service
public class OrderService {
private final UserRepository userRepo;
private final OrderRepository orderRepo;
// 构造器注入(推荐)
public OrderService(UserRepository userRepo, OrderRepository orderRepo) {
this.userRepo = userRepo;
this.orderRepo = orderRepo;
}
}ApplicationContext
java
// ApplicationContext 是 Spring IoC 容器的核心接口
// 常用实现:
// - AnnotationConfigApplicationContext(注解配置)
// - ClassPathXmlApplicationContext(XML 配置,旧式)
// - AnnotationConfigWebApplicationContext(Web 应用)
@Configuration
@ComponentScan("com.example")
public class AppConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
return ds;
}
}
// 启动容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
OrderService orderService = ctx.getBean(OrderService.class);Bean 的三种注入方式
java
@Service
public class UserService {
// 1. 构造器注入(推荐)
// 优点:依赖不可变,便于测试,避免循环依赖
private final UserRepository userRepo;
private final EmailService emailService;
public UserService(UserRepository userRepo, EmailService emailService) {
this.userRepo = userRepo;
this.emailService = emailService;
}
// 2. Setter 注入(可选依赖)
private NotificationService notificationService;
@Autowired(required = false)
public void setNotificationService(NotificationService ns) {
this.notificationService = ns;
}
// 3. 字段注入(不推荐,但常见)
// 缺点:无法在非 Spring 环境测试,隐藏依赖关系
@Autowired
private AuditService auditService;
}Bean 的作用域
java
// singleton(默认):容器中只有一个实例
@Bean
@Scope("singleton")
public UserService userService() { ... }
// prototype:每次获取都创建新实例
@Bean
@Scope("prototype")
public ShoppingCart shoppingCart() { ... }
// request:每个 HTTP 请求一个实例(Web 应用)
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public RequestContext requestContext() { ... }
// session:每个 HTTP Session 一个实例
@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserSession userSession() { ... }Bean 生命周期
实例化(Instantiation)
↓
属性填充(Populate Properties)
↓
BeanNameAware.setBeanName()
↓
BeanFactoryAware.setBeanFactory()
↓
ApplicationContextAware.setApplicationContext()
↓
BeanPostProcessor.postProcessBeforeInitialization()
↓
@PostConstruct / InitializingBean.afterPropertiesSet() / init-method
↓
BeanPostProcessor.postProcessAfterInitialization()
↓
Bean 就绪,可以使用
↓
容器关闭
↓
@PreDestroy / DisposableBean.destroy() / destroy-methodjava
@Component
public class DatabasePool implements InitializingBean, DisposableBean {
private ConnectionPool pool;
@PostConstruct
public void init() {
System.out.println("1. @PostConstruct 初始化");
pool = new ConnectionPool();
pool.initialize();
}
@Override
public void afterPropertiesSet() {
System.out.println("2. InitializingBean.afterPropertiesSet()");
}
@PreDestroy
public void cleanup() {
System.out.println("3. @PreDestroy 清理");
pool.shutdown();
}
@Override
public void destroy() {
System.out.println("4. DisposableBean.destroy()");
}
}BeanPostProcessor — 扩展点
java
// BeanPostProcessor 是 Spring 最重要的扩展点之一
// AOP、@Autowired、@Value 等都通过它实现
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("初始化前: " + beanName);
return bean; // 可以返回代理对象
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("初始化后: " + beanName);
// AOP 就在这里创建代理对象
return bean;
}
}条件注册
java
// @Conditional — 条件化 Bean 注册
@Configuration
public class DataSourceConfig {
// 只在有 MySQL 驱动时注册
@Bean
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
public DataSource mysqlDataSource() { ... }
// 只在配置了 app.datasource.url 时注册
@Bean
@ConditionalOnProperty(prefix = "app.datasource", name = "url")
public DataSource customDataSource() { ... }
// 只在没有其他 DataSource Bean 时注册
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource defaultDataSource() { ... }
// 只在特定 Profile 激活时注册
@Bean
@Profile("production")
public DataSource productionDataSource() { ... }
}@Value 与 @ConfigurationProperties
java
// @Value 注入单个配置
@Component
public class AppConfig {
@Value("${app.name:MyApp}") // 带默认值
private String appName;
@Value("${app.max-connections:100}")
private int maxConnections;
@Value("#{systemProperties['user.home']}") // SpEL 表达式
private String userHome;
}
// @ConfigurationProperties 绑定配置组(推荐)
@ConfigurationProperties(prefix = "app.database")
@Component
public class DatabaseProperties {
private String url;
private String username;
private String password;
private int maxPoolSize = 10;
private Duration connectionTimeout = Duration.ofSeconds(30);
// getter/setter 或使用 @ConstructorBinding(不可变)
}yaml
# application.yml
app:
database:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: secret
max-pool-size: 20
connection-timeout: 30s事件机制
java
// 自定义事件
public class UserRegisteredEvent extends ApplicationEvent {
private final User user;
public UserRegisteredEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() { return user; }
}
// 发布事件
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public User register(RegisterRequest req) {
User user = createUser(req);
eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
return user;
}
}
// 监听事件
@Component
public class EmailNotificationListener {
@EventListener
public void onUserRegistered(UserRegisteredEvent event) {
sendWelcomeEmail(event.getUser());
}
// 异步监听
@EventListener
@Async
public void onUserRegisteredAsync(UserRegisteredEvent event) {
sendSmsNotification(event.getUser());
}
// 条件监听
@EventListener(condition = "#event.user.vip == true")
public void onVipUserRegistered(UserRegisteredEvent event) {
sendVipGift(event.getUser());
}
}IoC 最佳实践
- 优先使用构造器注入,保证依赖不可变
- 避免循环依赖(构造器注入会在启动时报错,及早发现)
- 使用
@ConfigurationProperties而非大量@Value - 合理使用
@Profile区分环境配置 - 善用
@Conditional实现灵活的条件装配