【Spring全家桶】Spring Framework核心:IoC容器:Bean生命周期、作用域、依赖注入DI、循环依赖与三级缓存(附《思维导图》+《面试高频考点清单》)

简介: Spring IoC容器是Spring框架的核心,通过控制反转(IoC)与依赖注入(DI)实现对象解耦:容器统一管理Bean生命周期、依赖关系及作用域(如singleton、prototype等),支持构造/Setter/字段注入,并以三级缓存机制解决循环依赖,大幅提升代码可测试性、可维护性与扩展性。

思维导图

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容器启动流程

  1. 配置解析阶段:读取XML、注解或Java配置,解析为BeanDefinition
  2. BeanDefinition注册阶段:将BeanDefinition注册到BeanDefinitionRegistry
  3. 容器刷新阶段:调用refresh()方法,完成容器初始化
  4. Bean实例化阶段:根据BeanDefinition创建Bean实例
  5. 依赖注入阶段:注入Bean之间的依赖关系
  6. 初始化阶段:执行Bean的初始化方法
  7. 容器就绪阶段:容器启动完成,可对外提供服务

二、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
  • 三种循环依赖场景:

    1. 构造方法循环依赖:无法解决,抛出BeanCurrentlyInCreationException
    2. setter方法循环依赖(单例):可通过三级缓存解决
    3. 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)

  1. 创建A实例:调用构造函数实例化A,此时A未完成属性填充和初始化
  2. 将A的工厂放入三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))
  3. 填充A的属性B:发现A依赖B,去容器中获取B
  4. 创建B实例:调用构造函数实例化B
  5. 将B的工厂放入三级缓存
  6. 填充B的属性A:发现B依赖A,去容器中获取A
  7. 从三级缓存获取A的早期引用:调用ObjectFactory.getObject()生成A的早期引用
  8. 将A的早期引用放入二级缓存:从三级缓存移除A的工厂
  9. B完成属性填充和初始化:B创建完成,放入一级缓存
  10. A继续属性填充:注入已完成的B
  11. A完成初始化:A创建完成,放入一级缓存,从二级缓存移除A的早期引用

5.4 为什么需要三级缓存而不是两级?

  • 核心原因:支持AOP代理的循环依赖
  • 如果只有二级缓存:
    • 当Bean需要被AOP代理时,注入的应该是代理对象而不是原始对象
    • 但在实例化阶段无法确定Bean是否需要被代理
    • 三级缓存的ObjectFactory可以在需要时动态生成代理对象
  • 三级缓存的本质:延迟生成代理对象,只有在发生循环依赖时才提前生成代理

5.5 循环依赖解决方案

  1. 重构代码:消除循环依赖,这是最根本的解决方案
  2. 使用setter注入:Spring默认支持singleton作用域的setter注入循环依赖
  3. 使用@Lazy注解:延迟初始化依赖的Bean,解决构造函数注入的循环依赖
  4. 使用@PostConstruct注解:在初始化方法中手动获取依赖
  5. 使用ApplicationContext.getBean():在需要时手动获取依赖

六、常见问题与最佳实践

6.1 常见问题

  1. Bean不是单例的原因

    • 作用域配置错误(prototype)
    • 多次实例化Bean(new关键字)
    • 父子容器重复创建
    • AOP代理导致的多实例
  2. 依赖注入失败的原因

    • Bean未被Spring扫描到(@ComponentScan配置错误)
    • 多个同类型Bean存在且未指定@Primary或@Qualifier
    • 循环依赖(构造方法注入)
    • 依赖的Bean未初始化完成
  3. Bean初始化顺序问题

    • 使用@DependsOn指定依赖关系
    • 实现SmartLifecycle接口控制启动顺序
    • 使用ApplicationListener监听容器事件

      6.2 最佳实践

  4. 优先使用构造函数注入:确保依赖不可变,便于单元测试
  5. 避免字段注入:不利于代码的可维护性和可测试性
  6. 合理使用作用域:根据Bean的状态和使用场景选择合适的作用域
  7. 避免循环依赖:通过重构代码消除循环依赖,提高代码质量
  8. 使用标准注解:优先使用JSR-250的@PostConstruct和@PreDestroy
  9. 避免在Bean中使用new关键字:所有对象都应由Spring容器管理
  10. 开启Bean验证:使用@Validated验证Bean属性
  11. 配置外部化:使用@Value和@ConfigurationProperties注入配置
  12. 使用@Profile:根据环境激活不同的Bean
  13. 使用@ConfigurationProperties代替@Value:批量注入配置属性,类型安全
  14. 使用条件注解:根据环境条件动态创建Bean
  15. 自定义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容器的完整启动流程

标准答案

  1. 配置解析阶段:读取XML、注解或Java配置,解析为BeanDefinition
  2. BeanDefinition注册阶段:将BeanDefinition注册到BeanDefinitionRegistry
  3. 容器刷新阶段:调用核心方法refresh(),完成容器初始化
  4. Bean实例化阶段:根据BeanDefinition通过反射创建Bean实例
  5. 依赖注入阶段:自动注入Bean之间的依赖关系
  6. 初始化阶段:执行Bean的各种初始化方法
  7. 容器就绪阶段:容器启动完成,可对外提供服务

Q4:IoC容器的核心组件有哪些?各自的职责是什么?

标准答案

  • BeanDefinition:存储Bean的元数据信息(类名、属性、构造参数、作用域、初始化方法等)
  • BeanFactory:最基础的容器接口,提供Bean的生命周期管理核心功能
  • ApplicationContext:BeanFactory的扩展,提供企业级功能
  • BeanDefinitionReader:读取配置信息并转换为BeanDefinition
  • BeanPostProcessor:Bean初始化前后的拦截器,可自定义Bean的创建过程(如AOP代理生成)

二、Bean生命周期

Q5:请详细描述Spring Bean的完整生命周期(11步)

标准答案

  1. 实例化前:解析并注册BeanDefinition;调用InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
  2. 实例化:通过反射调用构造函数创建Bean实例;调用InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
  3. 属性填充:自动注入依赖的Bean(DI过程)
  4. Aware接口回调:依次执行BeanNameAwareBeanClassLoaderAwareBeanFactoryAwareApplicationContextAware
  5. 初始化前:调用BeanPostProcessor.postProcessBeforeInitialization(处理@PostConstruct注解)
  6. 初始化:执行InitializingBean.afterPropertiesSet接口方法
  7. 自定义初始化:执行配置中指定的init-method方法
  8. 初始化后:调用BeanPostProcessor.postProcessAfterInitialization(生成AOP代理)
  9. Bean就绪:Bean可以被正常使用
  10. 销毁前:容器关闭时,执行DisposableBean.destroy接口方法
  11. 自定义销毁:执行配置中指定的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行为。
  • 解决方案
    1. 使用ObjectFactory<T>Provider<T>延迟获取prototype Bean
    2. 使用方法注入(@Lookup注解)
    3. 实现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有哪些依赖注入类型?各自的优缺点是什么?

标准答案

  1. 构造函数注入(Spring 4.3+推荐)
    • 优点:依赖不可变、确保依赖不为空、便于单元测试、符合单一职责原则
    • 缺点:参数过多时构造函数臃肿
  2. Setter方法注入
    • 优点:支持可选依赖、允许重新注入
    • 缺点:依赖可变、可能出现空指针异常
  3. 字段注入(不推荐)
    • 优点:代码简洁
    • 缺点:依赖不可见、不利于单元测试、违反单一职责原则、无法注入final字段

Q12:为什么Spring推荐使用构造函数注入?

标准答案

  1. 依赖不可变:使用final关键字修饰依赖字段,防止被意外修改
  2. 确保依赖不为空:构造函数执行时会检查依赖是否存在,避免空指针异常
  3. 便于单元测试:不需要Spring容器,直接通过构造函数传入模拟对象即可测试
  4. 符合单一职责原则:构造函数参数过多时,会明显提示该类职责过重,需要重构
  5. 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不支持的循环依赖
    1. prototype作用域的循环依赖(每次创建新实例,无法缓存)
    2. 构造函数注入的循环依赖(除非使用@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的循环依赖解决流程

标准答案

  1. 创建A实例(构造函数执行完成,未填充属性)
  2. 将A的工厂放入三级缓存
  3. 填充A的属性B,去容器获取B
  4. 创建B实例,将B的工厂放入三级缓存
  5. 填充B的属性A,去容器获取A
  6. 从三级缓存获取A的早期引用,放入二级缓存
  7. B完成属性填充和初始化,放入一级缓存
  8. A继续属性填充,注入已完成的B
  9. A完成初始化,放入一级缓存,从二级缓存移除

Q21:循环依赖有哪些解决方案?

标准答案

  1. 重构代码:消除循环依赖(最根本、最推荐的解决方案)
  2. 使用setter注入:Spring默认支持singleton作用域的setter注入循环依赖
  3. 使用@Lazy注解:延迟初始化依赖的Bean,解决构造函数注入的循环依赖
  4. 使用@PostConstruct注解:在初始化方法中手动获取依赖
  5. 实现ApplicationContextAware:在需要时手动调用getBean()获取依赖

六、常见问题与最佳实践

Q22:Spring中常见的Bean相关异常有哪些?原因是什么?

标准答案

  • BeanCreationException:Bean创建失败,通常是由于依赖缺失、配置错误或初始化方法抛出异常
  • NoSuchBeanDefinitionException:容器中找不到指定类型或名称的Bean
  • NoUniqueBeanDefinitionException:存在多个同类型Bean,无法确定注入哪个
  • NullPointerException:依赖注入失败,导致字段为null
  • BeanCurrentlyInCreationException:循环依赖异常

Q23:Spring依赖注入的最佳实践有哪些?

标准答案

  1. 优先使用构造函数注入:确保依赖不可变,便于单元测试
  2. 避免字段注入:不利于代码的可维护性和可测试性
  3. 合理使用作用域:根据Bean的状态和使用场景选择合适的作用域
  4. 避免循环依赖:通过重构代码消除循环依赖,提高代码质量
  5. 使用@ConfigurationProperties代替@Value:批量注入配置属性,类型安全
  6. 使用条件注解:根据环境条件动态创建Bean
  7. 自定义BeanPostProcessor:扩展Bean的创建过程,实现自定义逻辑
相关文章
|
15天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
5716 29
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
10天前
|
存储 定位技术 数据库
CodeGraph 如何让 Claude Code减少 7 成工具调用?
CodeGraph 为 Coding Agent 提供本地代码知识图谱,把函数、类、调用链和框架路由提前整理成“项目地图”,减少盲目搜索和文件读取。它不是新 Agent,而是上下文基础设施,让 Agent 更快找到正确代码路径,平均减少 7 成工具调用。
1163 2
|
7天前
|
人工智能 安全 定位技术
CodeGraph深度解析 让Claude Code工具调用直降七成的核心原理与实操教程
如今以Claude Code为代表的AI编程智能体已经成为开发者日常编码、项目重构、漏洞修复的必备工具。但在长期使用过程中,几乎所有开发者都会遇到同一个明显痛点:AI虽然具备强大的代码生成与分析能力,却常常陷入盲目探索的循环中。
924 1
|
17天前
|
人工智能 自然语言处理 供应链
|
7天前
|
人工智能 弹性计算 安全
阿里云618活动时间、活动入口、优惠活动详细解读
2026年阿里云618创新加速季已全面开启,作为年度力度最大的云产品促销活动,本次大促覆盖轻量应用服务器、ECS云服务器、GPU云服务器、数据库、AI算力、安全服务、CDN等全品类产品,推出5亿元算力补贴、新用户限时秒杀、普惠满减、企业专享、免费试用、云大使返佣等多重福利,个人开发者、中小企业、AI团队均可享受专属低价。本文将系统梳理2026年阿里云618活动的完整时间节点、官方参与入口、各类优惠细则、使用规则、热门产品推荐及实操代码,帮助用户精准参与、高效省钱,以最低成本完成上云部署。
702 3
|
23天前
|
人工智能 开发工具 iOS开发
Claude Code 新手完全上手指南:安装、国产模型配置与常用命令全解
Claude Code 是一款运行在终端环境中的 AI 编程助手,能够直接在命令行中完成代码生成、项目分析、文件修改、命令执行、Git 管理等开发全流程工作。它最大的特点是**任务驱动、终端原生、轻量高效、多模型兼容**,无需图形界面、不依赖 IDE 插件,能够深度融入开发者日常工作流。
3825 15
|
8天前
|
运维
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
欢迎报名|2026 Agentic AICon—智能体基础设施与AgentOps专场,邀您参会
1419 0