跳到主要内容

IOC 容器

IOC(Inversion of Control,控制反转)是 Spring 框架的核心思想,也是理解 Spring 的关键。本章将深入讲解 IOC 的原理、Spring 容器的架构以及 Bean 的管理。

什么是 IOC?

IOC 全称是 Inversion of Control,中文译为"控制反转"。要理解这个概念,我们需要先了解传统程序设计中对象依赖关系的处理方式。

传统方式的问题

假设我们有一个用户服务类,需要调用用户数据访问类来获取用户信息:

public class UserService {
private UserDao userDao = new UserDaoImpl();

public User getUser(Long id) {
return userDao.findById(id);
}
}

这种写法存在几个明显的问题:

耦合度高:UserService 直接依赖 UserDaoImpl 具体实现类,如果要换成另一个实现,必须修改源代码。

难以测试:单元测试时无法轻松替换 UserDao 为 Mock 对象。

职责不清:UserService 除了业务逻辑,还负责创建和管理依赖对象。

IOC 的解决方案

IOC 的核心思想是:将对象的创建和依赖关系的管理交给容器来完成,而不是由对象自己控制。看看 IOC 模式下的写法:

public class UserService {
private UserDao userDao;

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

public User getUser(Long id) {
return userDao.findById(id);
}
}

UserService 不再自己创建 UserDao,而是通过 setter 方法接收外部注入的依赖。这就是"控制反转"——原来由程序代码主动控制的依赖关系,现在反转给容器来管理。

IOC 与 DI 的关系

很多人会混淆 IOC 和 DI(Dependency Injection,依赖注入)。实际上,DI 是 IOC 的一种具体实现方式。

IOC:是一种设计思想,指将对象创建和依赖关系管理的控制权从程序代码转移到外部容器。

DI:是 IOC 的实现方式之一,指在运行期由容器将依赖对象注入到组件中。

可以这样理解:IOC 是目的,DI 是手段。Spring 采用 DI 来实现 IOC。

Spring 容器架构

Spring 容器是 Spring 框架的核心,负责管理 Bean 的整个生命周期。Spring 提供了两种主要的容器类型。

BeanFactory

BeanFactory 是 Spring 最基础的容器接口,提供了基本的 IOC 功能:

public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}

BeanFactory 采用延迟初始化策略,只有在调用 getBean() 时才创建 Bean 实例。这种方式节省资源,适合资源受限的环境。

ApplicationContext

ApplicationContext 是 BeanFactory 的子接口,提供了更多企业级功能:

public interface ApplicationContext extends 
EnvironmentCapable,
ListableBeanFactory,
HierarchicalBeanFactory,
MessageSource,
ApplicationEventPublisher,
ResourcePatternResolver {
String getId();
String getApplicationName();
String getDisplayName();
long getStartupDate();
ApplicationContext getParent();
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

ApplicationContext 相比 BeanFactory 增加了以下功能:

国际化支持:实现 MessageSource 接口,支持多语言消息。

事件发布:实现 ApplicationEventPublisher 接口,支持事件驱动编程。

资源访问:实现 ResourcePatternResolver 接口,支持加载各种资源。

AOP 集成:自动检测并注册 BeanPostProcessor。

预初始化单例 Bean:容器启动时就创建所有单例 Bean,便于发现配置错误。

常用 ApplicationContext 实现

ClassPathXmlApplicationContext:从类路径加载 XML 配置文件。

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

FileSystemXmlApplicationContext:从文件系统加载 XML 配置文件。

ApplicationContext context = new FileSystemXmlApplicationContext("/path/to/applicationContext.xml");

AnnotationConfigApplicationContext:基于 Java 配置类创建容器。

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

AnnotationConfigWebApplicationContext:用于 Web 应用的注解配置容器。

容器的启动过程

理解 Spring 容器的启动过程有助于深入理解 IOC 的工作原理。

1. 准备阶段

容器创建后,首先进行准备工作:

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}

this() 调用无参构造器,创建 BeanDefinitionReader 和 ClassPathBeanDefinitionScanner。

register() 注册配置类。

refresh() 是容器启动的核心方法。

2. refresh() 方法详解

refresh() 方法定义在 AbstractApplicationContext 中,是容器启动的核心流程:

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

// 1. 准备工作:记录启动时间、初始化属性源
prepareRefresh();

// 2. 获取 BeanFactory,读取 BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 3. 配置 BeanFactory 的标准特性
prepareBeanFactory(beanFactory);

try {
// 4. 子类扩展点:允许修改 BeanFactory
postProcessBeanFactory(beanFactory);

StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");

// 5. 执行 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);

// 6. 注册 BeanPostProcessor
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();

// 7. 初始化消息源(国际化)
initMessageSource();

// 8. 初始化事件广播器
initApplicationEventMulticaster();

// 9. 子类扩展点:初始化特殊 Bean
onRefresh();

// 10. 注册事件监听器
registerListeners();

// 11. 实例化所有非懒加载的单例 Bean
finishBeanFactoryInitialization(beanFactory);

// 12. 完成刷新:发布相应事件
finishRefresh();
} catch (BeansException ex) {
// 销毁已创建的 Bean
destroyBeans();
cancelRefresh(ex);
throw ex;
} finally {
resetCommonCaches();
contextRefresh.end();
}
}
}

这个过程涉及很多概念,我们会在后续章节逐步展开。现在只需要了解容器启动时会读取配置、创建 BeanDefinition、实例化 Bean 等步骤。

BeanDefinition

BeanDefinition 是 Spring 中描述 Bean 的核心接口。它定义了 Bean 的各种属性,如类名、作用域、懒加载、依赖关系等。

BeanDefinition 的主要属性

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// 作用域
String SCOPE_SINGLETON = "singleton";
String SCOPE_PROTOTYPE = "prototype";

// 获取/设置 Bean 的类名
String getBeanClassName();
void setBeanClassName(String beanClassName);

// 获取/设置作用域
String getScope();
void setScope(String scope);

// 是否懒加载
boolean isLazyInit();
void setLazyInit(boolean lazyInit);

// 依赖的其他 Bean
String[] getDependsOn();
void setDependsOn(String... dependsOn);

// 是否自动装配候选
boolean isAutowireCandidate();
void setAutowireCandidate(boolean autowireCandidate);

// 是否首选 Bean
boolean isPrimary();
void setPrimary(boolean primary);

// 工厂方法名
String getFactoryMethodName();
void setFactoryMethodName(String factoryMethodName);

// 工厂 Bean 名
String getFactoryBeanName();
void setFactoryBeanName(String factoryBeanName);

// 构造函数参数
ConstructorArgumentValues getConstructorArgumentValues();

// 属性值
MutablePropertyValues getPropertyValues();

// 初始化方法
String getInitMethodName();
void setInitMethodName(String initMethodName);

// 销毁方法
String getDestroyMethodName();
void setDestroyMethodName(String destroyMethodName);
}

注册 BeanDefinition

可以通过编程方式向容器注册 BeanDefinition:

public class AppConfig {

@Bean
public static BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 创建 BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(UserService.class);
// 设置作用域
beanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON);
// 设置懒加载
beanDefinition.setLazyInit(true);
// 注册到容器
registry.registerBeanDefinition("userService", beanDefinition);
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
};
}
}

容器的使用示例

下面通过一个完整的示例演示 IOC 容器的使用。

定义接口和实现

package com.example.spring.dao;

public interface UserDao {
User findById(Long id);
void save(User user);
}
package com.example.spring.dao.impl;

import com.example.spring.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {

@Override
public User findById(Long id) {
return new User(id, "User-" + id);
}

@Override
public void save(User user) {
System.out.println("Saving user: " + user.getName());
}
}
package com.example.spring.service;

import com.example.spring.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {

private final UserDao userDao;

@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}

public User getUser(Long id) {
return userDao.findById(id);
}

public void saveUser(User user) {
userDao.save(user);
}
}

Java 配置类

package com.example.spring.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.example.spring")
public class AppConfig {
}

启动容器

package com.example.spring;

import com.example.spring.config.AppConfig;
import com.example.spring.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {
public static void main(String[] args) {
// 创建容器
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);

// 获取 Bean
UserService userService = context.getBean(UserService.class);

// 使用 Bean
User user = userService.getUser(1L);
System.out.println("User: " + user.getName());

// 关闭容器
context.close();
}
}

XML 配置方式

如果使用 XML 配置,创建 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="com.example.spring"/>
</beans>

启动容器:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

容器的继承关系

Spring 支持容器继承,子容器可以访问父容器中的 Bean,但父容器不能访问子容器中的 Bean。

// 父容器
AnnotationConfigApplicationContext parentContext =
new AnnotationConfigApplicationContext(ParentConfig.class);

// 子容器
AnnotationConfigApplicationContext childContext =
new AnnotationConfigApplicationContext();
childContext.setParent(parentContext);
childContext.register(ChildConfig.class);
childContext.refresh();

// 子容器可以访问父容器的 Bean
Object bean = childContext.getBean("parentBean");

这种机制在 Spring MVC 中有典型应用:Spring MVC 容器作为子容器,Spring 容器作为父容器。Controller 可以注入 Service,但 Service 不能注入 Controller。

小结

本章深入讲解了 Spring IOC 容器的核心概念:

  1. IOC 思想:将对象创建和依赖管理的控制权从程序代码转移到容器
  2. 容器架构:BeanFactory 提供基础功能,ApplicationContext 提供企业级功能
  3. 启动过程:refresh() 方法是容器启动的核心,包含多个阶段
  4. BeanDefinition:描述 Bean 的元数据,包含类名、作用域、依赖等属性
  5. 容器继承:子容器可以访问父容器的 Bean

下一章我们将学习依赖注入的各种方式,包括构造器注入、Setter 注入和字段注入。