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;       // 依赖的Bean
    ConstructorArgumentValues 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 {

    // 属性设置后调用
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化操作...");
    }

    // 容器关闭前调用
    @Override
    public 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 {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化前处理:" + beanName);
        return bean;
    }

    @Override
    public 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;

    // 构造器注入
    @Autowired
    public 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注入
    @Autowired
    public 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 {
    // 字段注入
    @Autowired
    private 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 {

    // 字段注入
    @Autowired
    private ProductRepository productRepository;

    // 构造器注入
    private final CategoryService categoryService;

    @Autowired
    public ProductServiceImpl(CategoryService categoryService) {
        this.categoryService = categoryService;
    }

    // setter注入
    private InventoryService inventoryService;

    @Autowired
    public 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/beans
                           http://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 {

    @Bean
    public UserRepository userRepository() {
        return new UserRepositoryImpl();
    }

    @Bean
    public 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 {
    @Autowired
    private 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. 创建BeanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // 3. 创建XML读取器
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);

        // 4. 加载、解析Bean定义并注册
        reader.loadBeanDefinitions(resource);

        // 5. BeanFactoryPostProcessor处理
        invokeBeanFactoryPostProcessors(beanFactory);

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

        // 7. 初始化国际化资源、事件多播器等
        initMessageSource();
        initApplicationEventMulticaster();

        // 8. 实例化所有非懒加载的单例Bean
        finishBeanFactoryInitialization(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. 初始化Bean
        instance = 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 {
    @Autowired
    private ServiceB serviceB;
}

@Service
public class ServiceB {
    @Autowired
    private 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 {
        @Bean
        public 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 无缝集成,支持面向切面编程

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月落霜满天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值