2-1 IoC 容器与依赖注入
概念解析
什么是 IoC?
IoC(Inversion of Control)控制反转:将对象的创建和管理权交给容器,而不是在代码中 new
DI(Dependency Injection)依赖注入:容器在运行时自动将依赖注入到对象中
Bean 注册方式
| 方式 | 注解 | 适用场景 |
|---|---|---|
| 组件扫描 | @Component, @Service, @Repository, @Controller | 大部分业务 Bean |
| @Bean 注解 | @Configuration + @Bean | 第三方库、需要精细控制 |
| @Import | @Import(ConfigClass.class) | 批量导入配置 |
| FactoryBean | 实现 FactoryBean 接口 | 复杂创建逻辑 |
| 动态注册 | BeanDefinitionRegistry | 动态注册Bean |
Bean 作用域
| 作用域 | 说明 | 使用场景 |
|---|---|---|
| singleton | 单例(默认) | 无状态 Bean |
| prototype | 原型,每次新建 | 有状态的 Bean |
| request | 一次请求 | Web 请求相关 |
| session | 一次会话 | 用户会话信息 |
| application | 应用级别 | 全局共享 |
| websocket | WebSocket 生命周期 | 长连接 |
代码示例
1. 基本注入方式
构造器注入(推荐)
@Service
public class UserService {
private final UserRepository userRepository;
// 构造器注入,final 不可变
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
Setter 注入
@Service
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
字段注入
@Service
public class UserService {
@Autowired
private UserRepository userRepository; // 不推荐
}
2. @Bean 方式注册
@Configuration
public class AppConfig {
@Bean
// 默认方法名就是 bean 名称
public UserService userService() {
return new UserServiceImpl();
}
@Bean(name = "customUserService") // 指定名称
public UserService userService2() {
return new UserServiceImpl();
}
}
3. @Configuration 深度解析
@Configuration
public class AppConfig {
@Bean
public UserService userService(UserRepository userRepository) {
// 参数会自动注入
return new UserServiceImpl(userRepository);
}
// @Scope 调整作用域
@Bean
@Scope("prototype")
public UserService prototypeService() {
return new UserServiceImpl();
}
// @Lazy 延迟初始化
@Bean
@Lazy
public UserService lazyService() {
return new UserServiceImpl();
}
// @Primary 优先使用
@Bean
@Primary
public UserRepository primaryRepository() {
return new PrimaryUserRepository();
}
}
4. Bean 条件注册
@Configuration
public class ConditionalConfig {
// 类存在时才注册
@Bean
@ConditionalOnClass(name = "com.fasterxml.jackson.databind.ObjectMapper")
public JsonService jsonService() {
return new JacksonJsonService();
}
// 配置项存在时才注册
@Bean
@ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true")
public CacheService cacheService() {
return new RedisCacheService();
}
// Bean 不存在时才注册
@Bean
@ConditionalOnMissingBean(CacheService.class)
public CacheService defaultCacheService() {
return new SimpleCacheService();
}
// 容器中有特定类型时才注册
@Bean
@ConditionalOnBean(DataSource.class)
public JdbcService jdbcService() {
return new JdbcServiceImpl();
}
}
5. Bean 生命周期回调
@Configuration
public class LifecycleConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup")
public DataSource dataSource() {
return new DruidDataSource();
}
// 或使用注解
@Component
public class MyService {
@PostConstruct
public void init() {
// 初始化操作
}
@PreDestroy
public void destroy() {
// 销毁操作
}
}
}
源码解读
Bean 创建流程
// AbstractApplicationContext.refresh() 核心流程
public void refresh() throws BeansException {
// 1. 准备 BeanFactory
prepareBeanFactory(beanFactory);
// 2. 执行 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 3. 注册 BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 4. 初始化消息源、事件广播器等
// 5. 创建 Bean(实例化 + 依赖注入)
finishBeanFactoryInitialization(beanFactory);
// 6. 初始化所有 Bean
finishRefresh();
}
Bean 实例化时机
| 类型 | 实例化时机 |
|---|---|
| singleton | 容器创建时(默认) |
| prototype | 每次 getBean 时 |
| lazy singleton | 第一次使用时 |
常见坑点
⚠️ 坑 1:循环依赖
问题:A 依赖 B,B 依赖 A
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a; // 循环依赖!
}
解决:
// 方案一:使用 @Lazy 延迟加载
@Autowired
private B b;
// 方案二:使用 Setter 注入(用于 break 循环)
@Component
public class A {
private B b;
@Autowired
public void setB(B b) {
this.b = b;
}
}
// 方案三:使用 @PostConstruct
@Component
public class A {
@Autowired
private B b;
@PostConstruct
public void init() {
b.setA(this);
}
}
注意:Spring Boot 2.6+ 默认禁止循环依赖,需要配置开启
⚠️ 坑 2:多个同类型 Bean 注入
问题:有多个实现类,@Autowired 时不知道注入哪个
// 有两个实现类
@Component
public class AlipayService implements PayService { }
@Component
public class WechatPayService implements PayService { }
// 注入时报错
@Autowired
private PayService payService; // NoUniqueBeanDefinitionException
解决:
// 方案一:@Primary 指定默认实现
@Component
@Primary
public class AlipayService implements PayService { }
// 方案二:@Qualifier 指定名称
@Autowired
@Qualifier("alipayService")
private PayService payService;
// 方案三:注入 List
@Autowired
private List<PayService> payServices; // 注入所有实现
⚠️ 坑 3:静态字段注入
问题:static 字段无法通过 @Autowired 注入
错误写法:
@Component
public class UserService {
@Autowired
private static UserRepository userRepository; // 错误!
}
正确写法:
@Component
public class UserService {
private static UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
UserService.userRepository = userRepository;
}
}
面试题
Q1:Spring IoC 的实现原理?
参考答案:
- 配置文件解析:读取 XML 或注解配置
- BeanDefinition:将 Bean 定义转为 BeanDefinition 对象
- BeanFactoryPostProcessor:允许在 Bean 创建前修改 BeanDefinition
- 实例化:通过反射(Constructor.newInstance)创建实例
- 依赖注入:通过反射设置属性值
- 初始化:执行 BeanPostProcessor 后置处理
- 生命周期管理:注册 DisposableBean 销毁回调
Q2:构造器注入为什么是推荐的?
参考答案:
- 不可变性:构造器注入的对象是 final,无法被修改
- 强制依赖:不提供构造器参数会导致编译失败
- 易于测试:可以直接 new 传入 mock 对象
- 线程安全:不存在中途被修改的风险
- 避免 NPE:构造器参数必须非空
Q3:BeanFactory 和 FactoryBean 的区别?
参考答案:
| 维度 | BeanFactory | FactoryBean |
|---|---|---|
| 定位 | IoC 容器接口 | 创建 Bean 的工厂 |
| 作用 | 管理 Bean 生命周期 | 封装复杂创建逻辑 |
| 获取方式 | getBean("beanName") | getBean("&beanName") |
| 使用场景 | 框架内部 | 开发者自定义 |
// FactoryBean 示例
@Component
public class MyFactoryBean implements FactoryBean<User> {
@Override
public User getObject() {
// 复杂创建逻辑
return new User();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
// 获取 MyFactoryBean 本身:ctx.getBean("&myFactoryBean")
// 获取 MyFactoryBean 创建的对象:ctx.getBean("myFactoryBean")
Q4:Bean 的生命周期?
参考答案:
1. 实例化 Bean
↓
2. 设置属性值(依赖注入)
↓
3. 检查 Aware 接口
↓
4. BeanPostProcessor.postProcessBeforeInitialization
↓
5. @PostConstruct 初始化方法
↓
6. InitializingBean.afterPropertiesSet
↓
7. 自定义 init-method
↓
8. BeanPostProcessor.postProcessAfterInitialization
↓
9. Bean 已就绪
↓
10. @PreDestroy 销毁方法
↓
11. DisposableBean.destroy
↓
12. 自定义 destroy-method
Q5:@Autowired 和 @Resource 的区别?
参考答案:
| 特性 | @Autowired | @Resource |
|---|---|---|
| 来源 | Spring 注解 | Java JSR-250 注解 |
| 注入方式 | byType + byName | byName 优先 |
| required 属性 | 支持 | 不支持 |
| 位置 | 字段/构造器/Setter | 字段/Setter |
// @Autowired 默认按类型匹配
@Autowired
private UserService userService;
// 可以配合 @Qualifier 指定名称
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;
// @Resource 默认按名称匹配,匹配失败再按类型
@Resource(name = "userServiceImpl")
private UserService userService;