
Spring Framework核心:IoC容器 系统性知识体系
一、IoC容器 整体架构与核心概念
1.1 IoC(控制反转)与DI(依赖注入)本质
- IoC——控制反转:将对象的创建、依赖管理、生命周期控制权从代码本身转移到Spring容器,实现了组件间的解耦
- DI——依赖注入:IoC的具体实现方式,容器通过构造函数、setter方法或字段注入对象之间的依赖关系
- 核心思想:面向接口编程,依赖抽象而非具体实现,提高代码可测试性、可扩展性和可维护性
- 核心优势:低耦合、高内聚、可测试性强、代码复用性高、便于 AOP 集成。
1.2 IoC容器体系结构
Spring IoC容器由两个核心接口定义:
- BeanFactory:基础容器,提供Bean的实例化、配置和管理功能,采用延迟初始化策略。
- ApplicationContext:高级容器,继承自BeanFactory,增加了国际化、事件发布、资源加载、AOP集成等功能,启动时预实例化所有单例Bean。
ApplicationContext常见实现类:
- ClassPathXmlApplicationContext:从类路径加载XML配置文件
- FileSystemXmlApplicationContext:从文件系统加载XML配置文件
- AnnotationConfigApplicationContext:基于注解配置的容器
- WebApplicationContext:Web应用专用容器
1.3 IoC容器核心组件
| 组件 | 职责 | 核心接口 |
|------|------|----------|
| BeanDefinition | 存储Bean的元数据信息(类名、属性、构造参数、作用域等) | BeanDefinition |
| BeanFactory | Spring最基础的容器接口,提供Bean的创建、获取、销毁等核心功能 | BeanFactory |
| ApplicationContext | BeanFactory的扩展,提供更多企业级功能(事件发布、国际化、资源加载等) | ApplicationContext |
| BeanDefinitionReader | 读取配置信息并转换为BeanDefinition | BeanDefinitionReader |
| BeanPostProcessor | Bean初始化前后的拦截器,可自定义Bean的创建过程 | BeanPostProcessor |
1.4 IoC容器启动流程
- 配置解析阶段:读取XML、注解或Java配置,解析为BeanDefinition
- BeanDefinition注册阶段:将BeanDefinition注册到BeanDefinitionRegistry
- 容器刷新阶段:调用
refresh()方法,完成容器初始化 - Bean实例化阶段:根据BeanDefinition创建Bean实例
- 依赖注入阶段:注入Bean之间的依赖关系
- 初始化阶段:执行Bean的初始化方法
- 容器就绪阶段:容器启动完成,可对外提供服务
二、Bean生命周期(完整11步流程)
2.1 完整生命周期11步拆解
实例化前 → 实例化 → 依赖注入 → 初始化前 → 初始化 → 初始化后 → 使用 → 销毁前 → 销毁
阶段1:实例化前(Instantiation Before)
- 执行
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() - 可在此处返回代理对象,跳过默认实例化流程
- 典型应用:AOP动态代理创建
阶段2:实例化(Instantiation)
- 根据BeanDefinition调用构造方法创建Bean实例
- 构造方法选择:默认无参构造 → @Autowired构造 → 带参数构造
- 实例化后执行
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() - 返回false可跳过后续属性填充
阶段3:依赖注入(Dependency Injection)
- 执行
InstantiationAwareBeanPostProcessor.postProcessProperties() - 填充@Autowired、@Value、@Resource等注解标注的属性
- 调用setter方法完成属性注入
- 处理@Lookup方法注入
阶段4:初始化前(Initialization Before)
- 执行
BeanPostProcessor.postProcessBeforeInitialization() - 处理@PostConstruct注解(由CommonAnnotationBeanPostProcessor实现)
- 处理@Autowired注解的方法注入
阶段5:初始化(Initialization)
- 执行
InitializingBean.afterPropertiesSet()接口方法 - 执行
@Bean(initMethod="xxx")指定的自定义初始化方法
阶段6:初始化后(Initialization After)
- 执行
BeanPostProcessor.postProcessAfterInitialization() - AOP代理在此处生成(如果Bean需要被代理)
- 这是Bean返回给容器前的最后处理机会
阶段7:使用阶段
- Bean已完全初始化,可正常使用
- 单例Bean:整个容器生命周期内只有一个实例
- 原型Bean:每次获取都会创建新实例
阶段8:销毁前(Destruction Before)
- 容器关闭时触发
- 处理@PreDestroy注解(由CommonAnnotationBeanPostProcessor实现)
阶段9:销毁(Destruction)
- 执行
DisposableBean.destroy()接口方法 - 执行
@Bean(destroyMethod="xxx")指定的自定义销毁方法
2.2 生命周期关键接口与注解对比
| 执行顺序 | 接口/注解 | 特点 | 适用场景 |
|---|---|---|---|
| 1 | @PostConstruct | JSR-250标准注解,优先级最高 | 推荐使用,与Spring解耦 |
| 2 | InitializingBean.afterPropertiesSet() | Spring接口,侵入性强 | 框架内部使用 |
| 3 | @Bean(initMethod) | 配置指定,无侵入性 | 第三方类库初始化 |
| --- | --- | --- | --- |
| 1 | @PreDestroy | JSR-250标准注解,优先级最高 | 推荐使用 |
| 2 | DisposableBean.destroy() | Spring接口,侵入性强 | 框架内部使用 |
| 3 | @Bean(destroyMethod) | 配置指定,无侵入性 | 第三方类库销毁 |
三、Bean作用域
3.1 标准作用域(6种)
| 作用域 | 说明 | 适用场景 |
|---|---|---|
| singleton(默认) | 容器中仅存在一个Bean实例 | 无状态的服务类、工具类 |
| prototype | 每次获取都创建一个新的Bean实例 | 有状态的对象、需要频繁创建销毁的对象 |
| request | 每个HTTP请求创建一个Bean实例 | Web应用中的请求处理对象 |
| session | 每个HTTP会话创建一个Bean实例 | 用户会话相关的对象 |
| application | 整个ServletContext生命周期内仅一个实例 | 应用级别的全局对象 |
| websocket | 每个WebSocket连接创建一个Bean实例 | WebSocket通信相关对象 |
3.2 作用域使用注意事项
- singleton依赖prototype问题:singleton Bean只会注入一次prototype Bean,导致prototype Bean表现为singleton
- 解决方案:使用
ObjectFactory<T>、Provider<T>或方法注入
- 解决方案:使用
- request/session作用域在非Web环境:会抛出异常,需添加
@Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS) - 作用域代理模式:
- ScopedProxyMode.INTERFACES:基于JDK动态代理
- ScopedProxyMode.TARGET_CLASS:基于CGLIB代理
四、依赖注入DI
4.1 依赖注入类型
方式1:构造方法注入(Spring 4.3+推荐)
@Service
public class UserService {
private final UserRepository userRepository;
// Spring 4.3+单构造方法可省略@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
- 优点:
- 依赖不可变(final修饰)
- 保证依赖不为null
- 便于单元测试
- 符合单一职责原则
- 缺点:循环依赖无法通过构造方法注入解决
方式2:setter方法注入
@Service
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
- 优点:支持可选依赖,可重新注入
- 缺点:依赖可变,无法保证不为null
方式3:字段注入(不推荐)
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
}
- 优点:代码简洁
- 缺点:
- 依赖可变,无法保证不为null
- 不利于单元测试
- 违反单一职责原则不易察觉
4.2 依赖注入方式
- 按类型注入(默认):根据Bean的类型自动匹配
- 按名称注入:当存在多个同类型Bean时,使用
@Qualifier("beanName")指定 - 按构造函数参数注入:Spring 4.3+支持构造函数参数名自动匹配
- @Primary注解:指定多个同类型Bean中的首选Bean
- @Resource注解:JSR-250标准,默认按名称注入,找不到再按类型注入
4.3 依赖注入高级特性
- 集合注入:注入List、Set、Map、Properties等集合类型
- 自动装配:
@Autowired(required=false)允许依赖不存在 - @Value注解:注入基本类型、字符串、表达式、配置文件值
- @ConfigurationProperties:批量注入配置文件属性
- 条件注入:
@Conditional系列注解,根据条件决定是否创建Bean
五、循环依赖与三级缓存
5.1 循环依赖定义
- 循环依赖:两个或多个Bean之间相互依赖,形成闭环
- 常见场景:A依赖B,B依赖A;A依赖B,B依赖C,C依赖A
三种循环依赖场景:
- 构造方法循环依赖:无法解决,抛出BeanCurrentlyInCreationException
- setter方法循环依赖(单例):可通过三级缓存解决
- prototype循环依赖:无法解决,抛出BeanCurrentlyInCreationException
Spring支持的循环依赖:仅支持singleton作用域的setter注入循环依赖
- 不支持的循环依赖:
- prototype作用域的循环依赖
- 构造函数注入的循环依赖(除非使用
@Lazy注解)
5.2 三级缓存机制(核心原理)
Spring通过三级缓存解决singleton Bean的setter注入循环依赖问题:
| 缓存名称 | 存储内容 | 作用 |
|---|---|---|
| 一级缓存(singletonObjects) | 完全初始化完成的Bean实例 | 对外提供已就绪的Bean |
| 二级缓存(earlySingletonObjects) | 已实例化但未完成初始化的Bean实例 | 解决循环依赖中提前暴露对象的问题 |
| 三级缓存(singletonFactories) | ObjectFactory类型的Bean工厂 | 生成Bean的早期引用,支持AOP代理 |
5.3 循环依赖解决流程(A依赖B,B依赖A)
- 创建A实例:调用构造函数实例化A,此时A未完成属性填充和初始化
- 将A的工厂放入三级缓存:
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)) - 填充A的属性B:发现A依赖B,去容器中获取B
- 创建B实例:调用构造函数实例化B
- 将B的工厂放入三级缓存
- 填充B的属性A:发现B依赖A,去容器中获取A
- 从三级缓存获取A的早期引用:调用ObjectFactory.getObject()生成A的早期引用
- 将A的早期引用放入二级缓存:从三级缓存移除A的工厂
- B完成属性填充和初始化:B创建完成,放入一级缓存
- A继续属性填充:注入已完成的B
- A完成初始化:A创建完成,放入一级缓存,从二级缓存移除A的早期引用
5.4 为什么需要三级缓存而不是两级?
- 核心原因:支持AOP代理的循环依赖
- 如果只有二级缓存:
- 当Bean需要被AOP代理时,注入的应该是代理对象而不是原始对象
- 但在实例化阶段无法确定Bean是否需要被代理
- 三级缓存的ObjectFactory可以在需要时动态生成代理对象
- 三级缓存的本质:延迟生成代理对象,只有在发生循环依赖时才提前生成代理
5.5 循环依赖解决方案
- 重构代码:消除循环依赖,这是最根本的解决方案
- 使用setter注入:Spring默认支持singleton作用域的setter注入循环依赖
- 使用@Lazy注解:延迟初始化依赖的Bean,解决构造函数注入的循环依赖
- 使用@PostConstruct注解:在初始化方法中手动获取依赖
- 使用ApplicationContext.getBean():在需要时手动获取依赖
六、常见问题与最佳实践
6.1 常见问题
Bean不是单例的原因:
- 作用域配置错误(prototype)
- 多次实例化Bean(new关键字)
- 父子容器重复创建
- AOP代理导致的多实例
依赖注入失败的原因:
- Bean未被Spring扫描到(@ComponentScan配置错误)
- 多个同类型Bean存在且未指定@Primary或@Qualifier
- 循环依赖(构造方法注入)
- 依赖的Bean未初始化完成
Bean初始化顺序问题:
- 使用@DependsOn指定依赖关系
- 实现SmartLifecycle接口控制启动顺序
- 使用ApplicationListener监听容器事件
6.2 最佳实践
- 优先使用构造函数注入:确保依赖不可变,便于单元测试
- 避免字段注入:不利于代码的可维护性和可测试性
- 合理使用作用域:根据Bean的状态和使用场景选择合适的作用域
- 避免循环依赖:通过重构代码消除循环依赖,提高代码质量
- 使用标准注解:优先使用JSR-250的@PostConstruct和@PreDestroy
- 避免在Bean中使用new关键字:所有对象都应由Spring容器管理
- 开启Bean验证:使用@Validated验证Bean属性
- 配置外部化:使用@Value和@ConfigurationProperties注入配置
- 使用@Profile:根据环境激活不同的Bean
- 使用@ConfigurationProperties代替@Value:批量注入配置属性,类型安全
- 使用条件注解:根据环境条件动态创建Bean
- 自定义BeanPostProcessor:扩展Bean的创建过程,实现自定义逻辑
Spring IoC容器 面试高频问答卡片(背诵版)
一、IoC容器整体架构与核心概念
Q1:什么是IoC(控制反转)和DI(依赖注入)?它们的关系是什么?
标准答案:
- IoC控制反转:将对象的创建、依赖管理、生命周期控制权从业务代码本身转移到Spring容器,实现组件间的解耦。
- DI依赖注入:IoC的具体实现方式,容器通过构造函数、setter方法或字段自动注入对象之间的依赖关系。
- 关系:IoC是设计思想,DI是该思想的实现手段;没有DI,IoC无法落地。
- 核心思想:面向接口编程,依赖抽象而非具体实现,提高代码可测试性、可扩展性和可维护性。
Q2:BeanFactory和ApplicationContext的区别是什么?
标准答案:
| 对比维度 | BeanFactory | ApplicationContext |
|----------|-------------|--------------------|
| 定位 | Spring最基础的容器接口 | BeanFactory的扩展,企业级容器 |
| 功能 | 仅提供Bean的创建、获取、销毁等核心功能 | 额外提供事件发布、国际化、资源加载、环境集成等企业级功能 |
| 初始化时机 | 懒加载:调用getBean()时才创建Bean | 预加载:容器启动时就创建所有singleton Bean |
| 适用场景 | 资源受限的环境(如移动设备、Applet) | 绝大多数Java EE应用场景 |
Q3:请简述IoC容器的完整启动流程
标准答案:
- 配置解析阶段:读取XML、注解或Java配置,解析为BeanDefinition
- BeanDefinition注册阶段:将BeanDefinition注册到BeanDefinitionRegistry
- 容器刷新阶段:调用核心方法
refresh(),完成容器初始化 - Bean实例化阶段:根据BeanDefinition通过反射创建Bean实例
- 依赖注入阶段:自动注入Bean之间的依赖关系
- 初始化阶段:执行Bean的各种初始化方法
- 容器就绪阶段:容器启动完成,可对外提供服务
Q4:IoC容器的核心组件有哪些?各自的职责是什么?
标准答案:
- BeanDefinition:存储Bean的元数据信息(类名、属性、构造参数、作用域、初始化方法等)
- BeanFactory:最基础的容器接口,提供Bean的生命周期管理核心功能
- ApplicationContext:BeanFactory的扩展,提供企业级功能
- BeanDefinitionReader:读取配置信息并转换为BeanDefinition
- BeanPostProcessor:Bean初始化前后的拦截器,可自定义Bean的创建过程(如AOP代理生成)
二、Bean生命周期
Q5:请详细描述Spring Bean的完整生命周期(11步)
标准答案:
- 实例化前:解析并注册BeanDefinition;调用
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation - 实例化:通过反射调用构造函数创建Bean实例;调用
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation - 属性填充:自动注入依赖的Bean(DI过程)
- Aware接口回调:依次执行
BeanNameAware→BeanClassLoaderAware→BeanFactoryAware→ApplicationContextAware - 初始化前:调用
BeanPostProcessor.postProcessBeforeInitialization(处理@PostConstruct注解) - 初始化:执行
InitializingBean.afterPropertiesSet接口方法 - 自定义初始化:执行配置中指定的
init-method方法 - 初始化后:调用
BeanPostProcessor.postProcessAfterInitialization(生成AOP代理) - Bean就绪:Bean可以被正常使用
- 销毁前:容器关闭时,执行
DisposableBean.destroy接口方法 - 自定义销毁:执行配置中指定的
destroy-method方法
核心记忆口诀:实例化→属性填充→Aware回调→初始化前→初始化→初始化后→销毁
Q6:@PostConstruct、InitializingBean和init-method的执行顺序是什么?
标准答案:
执行顺序:@PostConstruct注解方法 → InitializingBean.afterPropertiesSet()接口方法 → 自定义init-method方法
Q7:Spring中有哪些Aware接口?它们的作用是什么?
标准答案:
BeanNameAware:获取Bean在容器中的名称BeanClassLoaderAware:获取加载Bean的类加载器BeanFactoryAware:获取当前的BeanFactory容器ApplicationContextAware:获取当前的ApplicationContext容器EnvironmentAware:获取环境变量和配置信息ResourceLoaderAware:获取资源加载器ApplicationEventPublisherAware:获取事件发布器
三、Bean作用域
Q8:Spring Bean有哪些标准作用域?各自的特点和适用场景是什么?
标准答案:
| 作用域 | 特点 | 适用场景 |
|--------|------|----------|
| singleton(默认) | 容器中仅存在一个Bean实例 | 无状态的服务类、工具类、配置类 |
| prototype | 每次调用getBean()都创建一个新实例 | 有状态的对象、需要频繁创建销毁的对象 |
| request | 每个HTTP请求创建一个实例 | Web应用中的请求处理对象 |
| session | 每个HTTP会话创建一个实例 | 用户会话相关的对象(如购物车) |
| application | 整个ServletContext生命周期内仅一个实例 | 应用级别的全局对象 |
| websocket | 每个WebSocket连接创建一个实例 | WebSocket通信相关对象 |
Q9:singleton Bean依赖prototype Bean会出现什么问题?如何解决?
标准答案:
- 问题:singleton Bean只会在容器启动时被创建一次,因此它依赖的prototype Bean也只会被注入一次,导致prototype Bean表现为singleton行为。
- 解决方案:
- 使用
ObjectFactory<T>或Provider<T>延迟获取prototype Bean - 使用方法注入(
@Lookup注解) - 实现
ApplicationContextAware接口,在需要时手动调用getBean()获取
- 使用
Q10:request/session作用域在非Web环境中使用会有什么问题?如何解决?
标准答案:
- 问题:会抛出
IllegalStateException异常,因为非Web环境没有对应的请求/会话上下文。 - 解决方案:添加作用域代理配置:
@Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS)ScopedProxyMode.INTERFACES:基于JDK动态代理(目标类实现接口时使用)ScopedProxyMode.TARGET_CLASS:基于CGLIB代理(目标类未实现接口时使用)
四、依赖注入DI
Q11:Spring有哪些依赖注入类型?各自的优缺点是什么?
标准答案:
- 构造函数注入(Spring 4.3+推荐)
- 优点:依赖不可变、确保依赖不为空、便于单元测试、符合单一职责原则
- 缺点:参数过多时构造函数臃肿
- Setter方法注入
- 优点:支持可选依赖、允许重新注入
- 缺点:依赖可变、可能出现空指针异常
- 字段注入(不推荐)
- 优点:代码简洁
- 缺点:依赖不可见、不利于单元测试、违反单一职责原则、无法注入final字段
Q12:为什么Spring推荐使用构造函数注入?
标准答案:
- 依赖不可变:使用final关键字修饰依赖字段,防止被意外修改
- 确保依赖不为空:构造函数执行时会检查依赖是否存在,避免空指针异常
- 便于单元测试:不需要Spring容器,直接通过构造函数传入模拟对象即可测试
- 符合单一职责原则:构造函数参数过多时,会明显提示该类职责过重,需要重构
- Spring 4.3+优化:单构造函数可以省略
@Autowired注解,代码更简洁
Q13:@Autowired和@Resource的区别是什么?
标准答案:
| 对比维度 | @Autowired | @Resource |
|----------|-----------|-----------|
| 来源 | Spring框架 | JSR-250标准(Java官方) |
| 注入方式 | 默认按类型注入 | 默认按名称注入,找不到再按类型注入 |
| 支持参数 | required=false(允许依赖不存在) | name、type(指定注入的名称或类型) |
| 适用范围 | 字段、构造函数、setter方法 | 字段、setter方法 |
Q14:@Primary和@Qualifier注解的作用是什么?
标准答案:
- @Primary:当容器中存在多个同类型Bean时,指定其中一个为首选Bean,优先被注入
- @Qualifier("beanName"):当容器中存在多个同类型Bean时,明确指定要注入的Bean名称,优先级高于@Primary
Q15:@Value和@ConfigurationProperties的区别是什么?
标准答案:
| 对比维度 | @Value | @ConfigurationProperties |
|----------|--------|--------------------------|
| 注入方式 | 单个属性逐个注入 | 批量注入配置文件中的一组属性 |
| 类型安全 | 不支持(类型转换失败会抛出异常) | 支持(自动进行类型转换和校验) |
| 松散绑定 | 不支持 | 支持(如user-name可以绑定到userName) |
| 复杂类型 | 不支持(无法注入Map、List等复杂类型) | 支持 |
| 适用场景 | 注入单个简单属性 | 注入一组相关的配置属性 |
五、循环依赖与三级缓存
Q16:什么是循环依赖?Spring支持哪些循环依赖?不支持哪些?
标准答案:
- 循环依赖:两个或多个Bean之间相互依赖,形成闭环(如A依赖B,B依赖A)
- Spring支持的循环依赖:仅支持singleton作用域的setter注入循环依赖
- Spring不支持的循环依赖:
- prototype作用域的循环依赖(每次创建新实例,无法缓存)
- 构造函数注入的循环依赖(除非使用
@Lazy注解延迟初始化)
Q17:Spring如何解决singleton Bean的setter注入循环依赖?
标准答案:
Spring通过三级缓存机制解决循环依赖问题,核心思想是提前暴露未完成初始化的Bean实例。
Q18:Spring的三级缓存分别是什么?各自的作用是什么?
标准答案:
| 缓存名称 | 存储内容 | 作用 |
|----------|----------|------|
| 一级缓存(singletonObjects) | 完全初始化完成的Bean实例 | 对外提供已就绪的Bean |
| 二级缓存(earlySingletonObjects) | 已实例化但未完成初始化的Bean实例 | 解决循环依赖中提前暴露对象的问题 |
| 三级缓存(singletonFactories) | ObjectFactory类型的Bean工厂 | 生成Bean的早期引用,支持AOP代理 |
Q19:为什么需要三级缓存而不是两级缓存?
标准答案:
- 核心原因:支持AOP代理的循环依赖
- 如果只有两级缓存:当Bean需要被AOP代理时,注入的将是原始对象而不是代理对象
- 三级缓存中的
ObjectFactory可以在需要时动态生成代理对象的早期引用,确保注入的是正确的代理对象
Q20:请简述A依赖B、B依赖A的循环依赖解决流程
标准答案:
- 创建A实例(构造函数执行完成,未填充属性)
- 将A的工厂放入三级缓存
- 填充A的属性B,去容器获取B
- 创建B实例,将B的工厂放入三级缓存
- 填充B的属性A,去容器获取A
- 从三级缓存获取A的早期引用,放入二级缓存
- B完成属性填充和初始化,放入一级缓存
- A继续属性填充,注入已完成的B
- A完成初始化,放入一级缓存,从二级缓存移除
Q21:循环依赖有哪些解决方案?
标准答案:
- 重构代码:消除循环依赖(最根本、最推荐的解决方案)
- 使用setter注入:Spring默认支持singleton作用域的setter注入循环依赖
- 使用@Lazy注解:延迟初始化依赖的Bean,解决构造函数注入的循环依赖
- 使用@PostConstruct注解:在初始化方法中手动获取依赖
- 实现ApplicationContextAware:在需要时手动调用
getBean()获取依赖
六、常见问题与最佳实践
Q22:Spring中常见的Bean相关异常有哪些?原因是什么?
标准答案:
BeanCreationException:Bean创建失败,通常是由于依赖缺失、配置错误或初始化方法抛出异常NoSuchBeanDefinitionException:容器中找不到指定类型或名称的BeanNoUniqueBeanDefinitionException:存在多个同类型Bean,无法确定注入哪个NullPointerException:依赖注入失败,导致字段为nullBeanCurrentlyInCreationException:循环依赖异常
Q23:Spring依赖注入的最佳实践有哪些?
标准答案:
- 优先使用构造函数注入:确保依赖不可变,便于单元测试
- 避免字段注入:不利于代码的可维护性和可测试性
- 合理使用作用域:根据Bean的状态和使用场景选择合适的作用域
- 避免循环依赖:通过重构代码消除循环依赖,提高代码质量
- 使用@ConfigurationProperties代替@Value:批量注入配置属性,类型安全
- 使用条件注解:根据环境条件动态创建Bean
- 自定义BeanPostProcessor:扩展Bean的创建过程,实现自定义逻辑