当前位置: 首页 > news >正文

02.Spring_IOC详解

Spring IOC 容器详解

一、什么是 IOC

1.1 控制反转的基本概念

IOC (Inversion of Control) 控制反转,是一种设计思想,将原本在程序中手动创建对象的控制权交由 Spring 框架来管理。

传统 Java 程序设计中,我们直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象;而 IOC 是由专门的容器来负责对象的创建和维护,我们只需要直接使用即可。

简单来说,IOC 就是对象的创建控制权被反转了

  • 传统方式:我们主动通过 new 来创建实例,自己管理对象的创建
  • IOC 方式:对象的创建交给了 IoC 容器,我们被动地接收 IoC 容器创建好的对象

1.2 IOC 能解决什么问题

IOC 主要解决了两个核心问题:

  1. 解耦:将对象和对象之间的相互依赖关系解耦,减少了模块之间的耦合度
  2. 统一管理:集中管理对象的创建和生命周期,避免重复创建或资源浪费

1.3 IOC 和 DI 的关系

DI (Dependency Injection) 依赖注入是实现 IOC 的一种重要手段:

  • IOC是一种思想,是目标
  • DI是这种思想的具体实现方式,是手段

简单理解:IOC 是设计思想,DI 是实现方式。

二、Spring IOC 容器

2.1 Spring 提供的两种容器类型

Spring 提供了两种实现 IOC 容器的方式:

1. BeanFactory(基础型 IOC 容器)

这是最基础、最简单的容器,仅提供了 DI 的基本功能:

  • 简单的对象工厂,维护了 Bean 定义和相互依赖关系
  • 懒加载:默认延迟实例化,只有当客户端请求 Bean 时才创建实例
  • 提供了基本的生命周期管理和对象创建
// 使用BeanFactory
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
MyBean myBean = (MyBean) beanFactory.getBean("myBean");
2. ApplicationContext(企业级 IOC 容器)

是 BeanFactory 的子接口,增加了更多企业级功能:

  • 预初始化:容器启动时就实例化所有单例 Bean
  • 提供了完整的生命周期管理
  • 支持国际化、资源访问、事件传播
  • 支持 AOP 整合
// 使用ApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
MyBean myBean = context.getBean("myBean", MyBean.class);

2.2 常用的 ApplicationContext 实现

实现类用途
ClassPathXmlApplicationContext从类路径加载 XML 配置
FileSystemXmlApplicationContext从文件系统加载 XML 配置
AnnotationConfigApplicationContext基于 Java 配置类加载
WebApplicationContextWeb 应用专用的 AppContext

2.3 容器的继承体系

Spring IOC 容器的核心接口关系:

BeanFactory (interface)↑
ListableBeanFactory (interface)↑
ApplicationContext (interface)↑
ConfigurableApplicationContext (interface)↑
具体实现类(ClassPathXmlApplicationContext等)

三、IOC 容器的初始化过程

Spring IOC 容器的初始化过程可分为三大步骤:

3.1 资源定位(Resource)

从各种来源(XML 文件、注解、Java 配置类等)加载配置信息。

// XML配置方式
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");// 注解方式
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

3.2 Bean 定义解析(BeanDefinition)

将用户定义的 Bean 配置信息解析成 Spring 内部的 BeanDefinition 数据结构。

// Bean定义包含的主要信息
class BeanDefinition {String beanClassName;     // Bean的完整类名boolean lazyInit;         // 是否懒加载String[] dependsOn;       // 依赖的BeanConstructorArgumentValues constructorArgumentValues; // 构造参数MutablePropertyValues propertyValues;   // 属性值String scope;             // 作用域String factoryBeanName;   // 工厂Bean名称String factoryMethodName; // 工厂方法名称// ...其他配置
}

3.3 Bean 定义注册

将解析好的 BeanDefinition 注册到容器,形成 Bean 定义注册表。

// 伪代码展示
Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
beanDefinitionMap.put(beanName, beanDefinition); // 注册Bean定义

3.4 Bean 初始化和依赖注入

当容器启动或请求 Bean 时,根据 BeanDefinition 创建 Bean 实例,并进行依赖注入。

四、Bean 的生命周期

Spring Bean 的完整生命周期非常丰富,主要阶段如下:

4.1 Bean 生命周期完整流程

  1. 实例化:创建 Bean 实例(调用构造函数)
  2. 属性赋值:设置属性(依赖注入)
  3. 初始化:初始化 Bean
    • Aware 接口相关方法调用(如 BeanNameAware、BeanFactoryAware 等)
    • BeanPostProcessor 的前置处理
    • 调用初始化方法(InitializingBean 接口和自定义 init 方法)
    • BeanPostProcessor 的后置处理
  4. 使用:Bean 可以被应用程序使用
  5. 销毁:当容器关闭时,调用销毁方法(DisposableBean 接口和自定义 destroy 方法)

4.2 生命周期扩展点

Spring 提供了多种扩展点让我们介入 Bean 的生命周期:

1. Bean 自身的方法

  • 实现InitializingBean接口:afterPropertiesSet()方法
  • 实现DisposableBean接口:destroy()方法
  • 自定义初始化/销毁方法(在 XML 或注解中配置)
public class MyBean implements InitializingBean, DisposableBean {// 属性设置后调用@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("初始化操作...");}// 容器关闭前调用@Overridepublic void destroy() throws Exception {System.out.println("清理资源...");}// 自定义初始化方法public void customInit() {System.out.println("自定义初始化...");}// 自定义销毁方法public void customDestroy() {System.out.println("自定义销毁...");}
}

2. 容器级的处理器

  • BeanPostProcessor:对 Bean 初始化前后进行处理
  • InstantiationAwareBeanPostProcessor:在实例化前后进行处理
  • BeanFactoryPostProcessor:对 BeanFactory 进行操作
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("初始化前处理:" + beanName);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("初始化后处理:" + beanName);return bean;}
}

五、依赖注入的方式

Spring 支持多种依赖注入方式,常用的有三种:

5.1 构造器注入

通过构造函数传入依赖,最符合依赖不可变原则,是官方推荐的注入方式。

@Service
public class UserServiceImpl implements UserService {private final UserRepository userRepository;// 构造器注入@Autowiredpublic UserServiceImpl(UserRepository userRepository) {this.userRepository = userRepository;}
}

XML 配置方式

<bean id="userService" class="com.example.UserServiceImpl"><constructor-arg ref="userRepository"/>
</bean>

5.2 Setter 注入

通过 setter 方法传入依赖,更具有灵活性,可以随时替换依赖。

@Service
public class OrderServiceImpl implements OrderService {private PaymentService paymentService;// setter注入@Autowiredpublic void setPaymentService(PaymentService paymentService) {this.paymentService = paymentService;}
}

XML 配置方式

<bean id="orderService" class="com.example.OrderServiceImpl"><property name="paymentService" ref="paymentService"/>
</bean>

5.3 字段注入

直接在字段上使用注解注入,最简洁但不推荐,因为不便于测试且隐藏依赖关系。

@Service
public class NotificationServiceImpl implements NotificationService {// 字段注入@Autowiredprivate EmailService emailService;
}

5.4 三种注入方式对比

注入方式优点缺点适用场景
构造器注入依赖不可变、强制依赖、便于测试参数过多时代码臃肿必须的依赖,核心业务组件
Setter 注入灵活、可选依赖、可修改不保证依赖完整性可选的依赖,外部配置
字段注入代码简洁隐藏依赖、难以测试简单场景,不推荐核心业务

六、自动装配

6.1 什么是自动装配

自动装配是 Spring 自动解析 Bean 之间依赖关系的过程,容器会自动查找、分析和注入依赖。

6.2 自动装配的方式

Spring 支持以下几种自动装配方式:

  1. byName:根据属性名称自动装配
  2. byType:根据属性类型自动装配
  3. constructor:类似于 byType,但应用于构造函数参数
  4. autodetect:先尝试 constructor,再尝试 byType
  5. no:默认值,不进行自动装配,通过显式配置注入

6.3 使用@Autowired 注解

@Autowired注解是最常用的自动装配手段,默认按类型注入:

@Service
public class ProductServiceImpl implements ProductService {// 字段注入@Autowiredprivate ProductRepository productRepository;// 构造器注入private final CategoryService categoryService;@Autowiredpublic ProductServiceImpl(CategoryService categoryService) {this.categoryService = categoryService;}// setter注入private InventoryService inventoryService;@Autowiredpublic void setInventoryService(InventoryService inventoryService) {this.inventoryService = inventoryService;}
}

6.4 处理自动装配中的歧义性

当存在多个相同类型的 Bean 时,可能导致自动装配歧义:

@Autowired
private PaymentService paymentService; // 错误:存在多个PaymentService实现

解决方法

  1. @Primary:标记首选的 Bean

    @Service
    @Primary
    public class AliPayServiceImpl implements PaymentService { }
    
  2. @Qualifier:指定 Bean 的名称

    @Autowired
    @Qualifier("aliPayService")
    private PaymentService paymentService;
    
  3. @Resource:按名称注入(Java 标准注解)

    @Resource(name = "aliPayService")
    private PaymentService paymentService;
    

七、Bean 的作用域

Spring 提供了多种 Bean 的作用域:

作用域说明使用场景
singleton默认值,每个容器中只有一个 Bean 实例大部分无状态的 Bean
prototype每次请求 Bean 时创建一个新实例有状态的 Bean
request每个 HTTP 请求创建一个实例Web 应用中的请求处理
session每个 HTTP 会话创建一个实例用户会话相关 Bean
application每个 ServletContext 创建一个实例应用级别的配置
websocket每个 WebSocket 创建一个实例WebSocket 应用

配置 Bean 的作用域

注解方式

@Component
@Scope("prototype")
public class ShoppingCart { /*...*/ }

XML 方式

<bean id="shoppingCart" class="com.example.ShoppingCart" scope="prototype"/>

八、Bean 的配置方式

Spring 支持多种 Bean 的配置方式:

8.1 XML 配置

最传统的方式,集中管理 Bean 定义。

<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userRepository" class="com.example.UserRepositoryImpl"/><bean id="userService" class="com.example.UserServiceImpl"><constructor-arg ref="userRepository"/></bean>
</beans>

8.2 Java 配置

使用@Configuration@Bean注解进行配置。

@Configuration
public class AppConfig {@Beanpublic UserRepository userRepository() {return new UserRepositoryImpl();}@Beanpublic UserService userService() {return new UserServiceImpl(userRepository());}
}

8.3 注解配置

使用组件扫描和注解自动检测和注册 Bean。

@SpringBootApplication // 包含@ComponentScan
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserRepository userRepository;// ...
}@Repository
public class UserRepositoryImpl implements UserRepository {// ...
}

8.4 三种配置方式的对比

配置方式优点缺点适用场景
XML 配置集中管理,修改不需重新编译繁琐,类型不安全第三方组件配置
Java 配置类型安全,重构友好修改需重新编译复杂配置,编程式配置
注解配置简洁,和代码放一起分散,不便整体查看业务组件自动装配

九、IOC 源码原理解析

9.1 IOC 容器的核心组件

Spring IOC 容器实现依赖于几个核心组件:

  1. BeanDefinition:保存 Bean 定义的元信息
  2. BeanDefinitionRegistry:管理 Bean 定义的注册表
  3. BeanFactory:Bean 工厂,负责创建和获取 Bean 实例
  4. PostProcessor:各种处理器,用于扩展容器功能

9.2 IOC 容器初始化流程简化版

ClassPathXmlApplicationContext为例:

// 伪代码表示初始化流程
public class ClassPathXmlApplicationContext {public ClassPathXmlApplicationContext(String configLocation) {// 1. 资源定位:加载配置文件Resource resource = new ClassPathResource(configLocation);// 2. 创建BeanFactoryDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 3. 创建XML读取器XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);// 4. 加载、解析Bean定义并注册reader.loadBeanDefinitions(resource);// 5. BeanFactoryPostProcessor处理invokeBeanFactoryPostProcessors(beanFactory);// 6. 注册BeanPostProcessorregisterBeanPostProcessors(beanFactory);// 7. 初始化国际化资源、事件多播器等initMessageSource();initApplicationEventMulticaster();// 8. 实例化所有非懒加载的单例BeanfinishBeanFactoryInitialization(beanFactory);// 9. 发布容器刷新事件finishRefresh();}
}

9.3 Bean 的创建过程简化版

// 伪代码表示Bean的创建过程
public Object createBean(String beanName, BeanDefinition beanDefinition) {try {// 1. 实例化前处理Object bean = resolveBeforeInstantiation(beanName, beanDefinition);if (bean != null) {return bean; // 如果前置处理返回了代理对象,直接使用}// 2. 创建Bean实例Object instance = createBeanInstance(beanName, beanDefinition);// 3. 属性赋值前处理applyMergedBeanDefinitionPostProcessors(beanName, instance, beanDefinition);// 4. 属性赋值(依赖注入)populateBean(beanName, instance, beanDefinition);// 5. 初始化Beaninstance = initializeBean(beanName, instance, beanDefinition);// 6. 处理循环依赖registerDisposableBeanIfNecessary(beanName, instance, beanDefinition);return instance;} catch (Exception e) {throw new BeanCreationException(beanName, "Creation of bean failed", e);}
}

十、常见问题与最佳实践

10.1 循环依赖问题

当两个或多个 Bean 相互依赖时会产生循环依赖:

@Service
public class ServiceA {@Autowiredprivate ServiceB serviceB;
}@Service
public class ServiceB {@Autowiredprivate ServiceA serviceA;
}

Spring 解决方案

  • 对于单例 Bean 的 setter 循环依赖,Spring 使用三级缓存解决
  • 对于原型 Bean 的循环依赖或构造器循环依赖,Spring 无法解决,会抛出异常

10.2 最佳实践

  1. 优先使用构造器注入:使依赖关系更明确,且可确保必要依赖完整性

    @Service
    public class UserServiceImpl implements UserService {private final UserRepository userRepository;public UserServiceImpl(UserRepository userRepository) {this.userRepository = userRepository;}
    }
    
  2. 合理使用 Bean 的作用域:大多数 Bean 默认单例即可,有状态 Bean 使用 prototype

    @Component
    @Scope("prototype")
    public class ShoppingCart { /*...*/ }
    
  3. 避免滥用组件扫描:控制扫描范围,避免意外注册不需要的 Bean

    @ComponentScan(basePackages = {"com.example.service", "com.example.repository"})
    
  4. 减少 XML 配置:优先使用 Java 配置和注解

    @Configuration
    public class AppConfig {@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");// ...return dataSource;}
    }
    
  5. 使用@Qualifier 避免歧义:当有多个同类型 Bean 时指定明确的名称

    @Autowired
    @Qualifier("mysqlDataSource")
    private DataSource dataSource;
    

十一、总结

Spring IOC 容器是 Spring 框架的核心,通过控制反转和依赖注入的设计思想,实现了对象的创建和依赖关系管理的控制权由程序代码转移到容器。它为 Java 应用程序提供了松耦合、可测试性和模块化的基础。

主要特点和优势:

  • 松耦合设计:组件之间的依赖关系被容器管理,降低了组件间的耦合度
  • 声明式编程:通过配置声明组件间的依赖关系,而非硬编码
  • 集中管理:统一管理所有组件的生命周期
  • 易于测试:可以轻松替换组件的实现,便于单元测试
  • AOP 支持:与 Spring AOP 无缝集成,支持面向切面编程

参考资料

  • Spring 基础 - Spring 核心之控制反转(IOC)
  • Spring 基础 - Spring IOC 实现原理详解之 IOC 体系结构设计
  • Spring 基础 - Spring IOC 实现原理详解之 Bean 实例化
  • Spring 官方文档
http://www.xdnf.cn/news/7795.html

相关文章:

  • Evidential Deep Learning和证据理论教材的区别(主要是概念)
  • test ssl java
  • 【C++指南】哈希驱动的封装:如何让unordered_map/set飞得更快更稳?【上】
  • 数据结构学习笔记 :二叉搜索树与高效查找算法详解
  • React 列表渲染基础示例
  • DFS/BFS专练-搞定图论基础!(从海岛问题过渡至图论基础应用C++/C)
  • 无刷电机槽数相同、转子极数不同的核心区别
  • Nacos安装及数据持久化
  • ESP32之本地HTTP服务器OTA固件升级流程,基于VSCode环境下的ESP-IDF开发(附源码)
  • 【Spring Boot】MyBatis入门:连接Mysql数据库、测试单元、连接的常见错误
  • 汇编语言中的数据
  • 基于C++(MFC)的细胞识别程序
  • 人工智能在后端开发中的革命:从架构到运维
  • 前端:uniapp中uni.pageScrollTo方法与元素的overflow-y:auto之间的关联
  • 极狐GitLab 项目导入导出设置介绍?
  • 架构师面试(三十一):IM 消息收发逻辑
  • 手撕STL——vector
  • 利用DeepSeek设计一个HTML批量转换工具设计
  • Hadoop的三大结构及其作用?
  • hadoop的三大结构及各自的作用
  • yarn的定义
  • 「数据可视化 D3系列」入门第九章:交互式操作详解
  • 自动驾驶与机器人算法学习
  • 【区块链通用服务平台及组件】京北方分布式身份管理平台 | FISCO BCOS 应用案例
  • java八股之并发编程
  • 医院数据中心智能化数据上报与调数机制设计
  • 仿腾讯会议项目开发——界面关闭功能实现
  • Flink介绍——实时计算核心论文之Kafka论文详解
  • java输出、输入语句
  • Vue3 Composition API与十大组件开发案例详解