IoC 简介
IoC 是什么
IoC 即控制反转 (Inversion of Control),也被称为依赖倒置原则,是设计模式六大原则之一。其核心思想是程序应依赖抽象接口,而非具体实现,从而降低代码间的耦合度。
IoC 的实现方式包括两种:
- 依赖注入(DI):不通过类内部直接创建依赖对象,而是将外部创建好的依赖对象通过构造函数或参数传递给类使用。
- 依赖查找:受控对象通过容器 API 查找所需资源和协作对象。
理解 IoC 的关键在于两点:
- 谁控制谁,控制什么:传统编程中,对象由程序主动创建;而 IoC 中,对象由容器控制创建,容器负责管理外部资源获取。
- 为何反转,哪些方面反转:传统方式是程序主动获取依赖对象(正转),而 IoC 中容器负责创建并注入依赖对象(反转),依赖对象的获取方式发生了反转。
IoC 的作用
IoC 是一种编程思想,而非具体技术,作为面向对象编程的重要法则,它指导设计松耦合的程序。传统方式中,类内部主动创建依赖对象导致高耦合,难以测试;IoC 将对象的创建和查找交由容器管理,使对象间松散耦合,便于测试和复用,提升程序体系结构的灵活性。IoC 体现了"主从换位"思想,应用程序从主动获取资源变为被动接受容器注入,符合"好莱坞法则":别找我们,我们找你。
IoC 与 DI
IoC 和依赖注入(DI)是从不同角度描述同一概念。IoC 强调控制权的反转,而 DI 更明确地指出依赖对象由容器注入。2004 年,Martin Fowler 提出"依赖注入"这一术语,以更清晰地表达这一过程。
IoC 容器
IoC 容器是具备依赖注入功能的容器,负责实例化、定位、配置应用程序中的对象并管理其依赖关系。在 Spring 中,BeanFactory
是 IoC 容器的基础实现,通过配置文件(如 XML、注解或 Java 代码)定义对象的元数据,容器据此完成对象的组装。
Bean
由 IoC 容器管理的对象称为 Bean。Bean 是容器初始化、装配和管理的对象,其定义通过配置元数据(如 BeanDefinition
)指定,描述如何实例化和组装。
Spring IoC
Spring IoC 容器通过构造函数参数、工厂方法参数或属性设置定义 Bean 的依赖关系,创建时由容器注入这些依赖。这一过程是对象主动控制依赖的反转,因此称为控制反转。核心包 org.springframework.beans
和 org.springframework.context
提供了 IoC 容器的基础支持。
IoC 容器
Spring 提供两种 IoC 容器:
- BeanFactory:基础 IoC 容器,提供配置框架和基本功能。
- ApplicationContext :
BeanFactory
的子接口,扩展了国际化、资源访问、事件机制等功能,适用于大多数场景。
实际开发中,推荐使用 ApplicationContext
,因其功能更丰富。ApplicationContext
接口负责实例化、配置和组装 Bean,通过配置元数据(XML、注解或 Java 代码)获取指令。
常见实现包括:
ClassPathXmlApplicationContext
:从类路径加载配置。FileSystemXmlApplicationContext
:从文件系统加载配置。
使用 IoC 容器的步骤包括:
- 配置元数据:定义容器如何初始化和管理 Bean。
- 实例化容器 :容器解析元数据,生成
BeanDefinition
并完成 Bean 的实例化与组装。 - 使用容器:客户端通过容器获取所需 Bean。
配置元数据
配置元数据描述 Bean 的属性和依赖,可通过以下方式实现:
- XML 配置 :传统方式,使用
<bean>
元素定义。 - 注解配置:Spring 2.5 引入,通过注解简化配置。
- Java 配置:Spring 3.0 引入,使用 Java 代码定义。
实例化容器
通过指定资源路径(如 XML 文件)实例化 ApplicationContext
,例如:
java
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
支持通过多个 XML 文件分层定义 Bean,使用 <import/>
元素组合:
xml
<beans>
<import resource="services.xml"/>
<bean id="bean1" class="..."/>
</beans>
使用容器
通过 getBean
方法获取 Bean 实例:
java
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml");
PetStoreService service = context.getBean("petStore", PetStoreService.class);
IoC 配置元数据
XML 配置
XML 配置是 Spring 的传统方式,基本结构如下:
xml
<beans>
<bean id="bean1" class="..."/>
<alias alias="bean3" name="bean2"/>
<import resource="resource.xml"/>
</beans>
<bean>
定义 Bean,id
为唯一标识,class
指定类名。<alias>
设置别名。<import>
导入其他配置文件。
实例化方式:
java
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"services.xml", "daos.xml"});
注解配置
Spring 2.5 引入注解配置,需在 XML 中启用:
xml
<context:annotation-config/>
常用注解包括:
@Required
:修饰 setter 方法,确保属性在 XML 中配置。@Autowired
:修饰属性、setter 或构造函数,按类型自动注入。@Qualifier
:与@Autowired
配合,按名称指定 Bean。@Resource
:按名称注入,支持 JSP 250 标准。@PostConstruct
和@PreDestroy
:定义生命周期方法。@Inject
:Spring 3.0 支持的 JSR 330 注解,功能类似@Autowired
。
Java 配置
Spring 3.0 引入 Java 配置,使用 @Configuration
和 @Bean
注解:
java
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
@Configuration
标记配置类。@Bean
定义 Bean,等价于 XML 中的<bean>
。
实例化:
java
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
依赖解决过程
容器解析 Bean 依赖的步骤:
- 使用配置元数据创建和初始化
ApplicationContext
。 - 为每个 Bean 提供依赖(属性或构造函数参数)。
- 将值或引用转换为实际类型。
Spring 在容器加载时验证配置,延迟设置属性以避免循环依赖问题。单例 Bean 默认预实例化,可通过配置改为延迟初始化。