本节我们主要研究的是:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中
Spring IOC实现原理详解之IOC初始化流程
Spring的IOC(控制反转)容器通过管理应用中的对象(即Bean)的生命周期和依赖关系,实现了松耦合、模块化的设计。在Spring中,IOC容器负责创建对象、管理对象之间的依赖关系以及执行生命周期的各个阶段。本文将详细解析Spring IOC的初始化流程,帮助大家深入理解Spring容器的工作原理。
一、Spring IOC容器初始化流程概述
Spring IOC容器初始化过程可简要总结为以下几个步骤:
1. 容器创建与加载配置
2. 读取Bean定义(注册Bean)
3. 实例化Bean对象
4. 填充Bean的依赖(依赖注入)
5. 执行Bean初始化方法
6. 容器准备就绪,提供服务
7. 容器关闭,销毁Bean(如果需要)
二、Spring IOC容器初始化流程详解
- 容器创建与加载配置
Spring容器通过 ApplicationContext 或 BeanFactory 接口来实现,最常见的是 ApplicationContext。创建容器时,容器会从配置文件或注解类中加载Bean定义。
• XML配置方式:通过解析XML文件(如applicationContext.xml),获取Bean的定义。
• 注解方式:通过注解(如@configuration、@componentscan)扫描并注册Bean。
容器的常见实现类:
• ClassPathXmlApplicationContext:用于加载XML配置文件。
• AnnotationConfigApplicationContext:用于Java配置类(通过注解)来配置Spring容器。
例如,使用XML配置的初始化方式如下:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
或者使用Java配置类:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
- 读取Bean定义(注册Bean)
Spring会读取配置文件或注解扫描的类,并解析其中的Bean定义。每个Bean定义会被注册到BeanDefinitionRegistry中。一个BeanDefinition包括Bean的类名、构造函数、依赖关系、作用域等信息。
例如,XML中的Bean定义:
这段XML表示容器中有一个id为myBean,类型为com.example.MyBean的Bean,并且设置了一个属性someProperty的值为someValue。
容器通过 BeanDefinitionReader 类来读取XML配置并将这些Bean定义注册到BeanDefinition容器中。
- 实例化Bean对象
在Spring容器初始化过程中,容器会根据BeanDefinition来实例化Bean。Spring支持懒加载(lazy loading)和即时加载(eager loading)两种方式:
• 懒加载:只有在第一次访问时,才实例化Bean。
• 即时加载:容器启动时即实例化所有单例Bean。
在实例化Bean时,Spring容器会使用反射机制调用无参构造方法来创建对象。对于需要依赖注入的Bean,Spring会在此阶段记录Bean的依赖关系。
MyBean myBean = new MyBean();
- 填充Bean的依赖(依赖注入)
在Bean实例化之后,Spring容器会进行依赖注入(DI,Dependency Injection)。Spring支持以下几种注入方式:
• 构造器注入:通过构造函数将依赖项注入Bean。
• Setter方法注入:通过调用Setter方法来注入依赖。
• 字段注入:通过反射直接设置字段。
Spring会根据BeanDefinition中的依赖关系(如、或注解标记)来确定如何注入Bean的依赖。
例如,XML配置的Setter注入:
构造器注入:
public class MyBean {
private Service service;
public MyBean(Service service) {
this.service = service;
}
}
依赖注入是通过调用Bean的Setter方法或构造方法来实现的。Spring会使用反射机制将依赖注入到Bean实例中。
- 执行Bean的初始化方法
当所有的依赖注入完成后,Spring会执行Bean的初始化方法。这些初始化方法可以通过以下方式指定:
• @PostConstruct注解:在Bean实例化和依赖注入之后,自动调用初始化方法。
• init-method属性:在XML配置中指定初始化方法。
• InitializingBean接口:实现afterPropertiesSet()方法来自定义初始化逻辑。
例如,使用@PostConstruct注解:
public class MyBean {
@PostConstruct
public void init() {
System.out.println("Bean has been initialized");
}
}
或者在XML配置中指定:
init-method属性指定的初始化方法将在依赖注入完成之后执行。
- 容器准备就绪,提供服务
在所有的Bean实例化、依赖注入和初始化方法执行完毕后,Spring容器就已经准备好提供服务了。此时可以从容器中获取Bean,并开始使用它们。
MyBean myBean = context.getBean("myBean", MyBean.class);
在这个阶段,Spring容器管理的所有Bean都可以通过容器进行访问,并且可以开始参与业务逻辑的处理。
- 容器关闭,销毁Bean(如果需要)
如果是单例Bean,当容器关闭时,Spring会销毁这些Bean,并执行销毁方法。销毁方法的定义方式有:
• @PreDestroy注解:标注销毁方法。
• destroy-method属性:在XML配置中指定销毁方法。
• DisposableBean接口:实现destroy()方法来自定义销毁逻辑。
例如,使用@PreDestroy注解:
public class MyBean {
@PreDestroy
public void destroy() {
System.out.println("Bean is being destroyed");
}
}
或者在XML配置中指定销毁方法:
容器关闭时,会根据这些配置调用销毁方法,释放相关资源。
三、总结
Spring IOC初始化流程是一个复杂且精细的过程,涉及了多个阶段的操作,从容器的创建、配置加载、Bean定义的注册、Bean实例化到依赖注入、初始化、以及容器关闭时的销毁过程。每个阶段都离不开Spring容器内部的机制和设计思想,如反射、依赖注入、Bean生命周期管理等。
理解这些初始化流程和背后的实现原理,对于开发者在实际开发中使用Spring框架至关重要。掌握这些细节,能够帮助我们更好地利用Spring容器进行高效的开发和调试,并能够解决更复杂的开发场景中的问题。
四、补充重要知识点
1.首先在初始化容器的时候,调用父类容器AbstractApplicationContext的构造方法(super(parent)方法)为容器设置好Bean资源加载器
通过AbstractApplicationContext默认构造函数初始化容器id, name, 状态 以及 资源解析器
通过AbstractApplicationContext的setParent(parent)方法将父容器的Environment合并到当前容器
在设置容器的资源加载器之后,接下来FileSystemXmlApplicationContet执行setConfigLocations方法通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean定义资源文件的定位
2.初始化的主题流程是从refresh方法开始的,refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器
在refresh方法中,有很多细节,首先 synchronized (this.startupShutdownMonitor)
• 使用 synchronized 锁定 startupShutdownMonitor 对象,以确保在容器的刷新过程中不会有并发调用。
• startupShutdownMonitor 是一个同步对象,用于控制在 Spring 容器的启动和关闭过程中线程的安全性。
这里面有对应的模版方法和钩子方法:
模板方法 是指在父类中定义一个算法的框架,具体的实现由子类来完成。它允许子类在不改变算法结构的情况下实现具体的步骤。
在 Spring 中,refresh() 方法本身就是一个 模板方法,它定义了容器初始化的整体流程,但其中的部分步骤是由子类实现的。refresh() 方法中的调用顺序固定,而一些具体的实现留给子类来执行。
在这段代码中,refresh() 方法按照固定的步骤进行容器的初始化,但一些具体的步骤是由子类(如 AnnotationConfigApplicationContext 或其他具体的 ApplicationContext 实现)来定义和实现。
钩子方法(Hook Method)
钩子方法 是指在父类中定义的方法,允许子类选择性地重写或执行。钩子方法通常是空的或提供默认行为,但子类可以根据需要覆盖它们来扩展父类的功能。
在这段代码中,有几个方法是典型的钩子方法,它们允许子类根据具体需求进行扩展或修改行为:
• onRefresh():
这是一个钩子方法,它在容器刷新时被调用,但默认实现为空。具体的实现逻辑通常由子类提供,用来扩展容器刷新过程中的行为。例如,AnnotationConfigApplicationContext 就会覆盖这个方法来执行特定的初始化逻辑。
本节我们主要研究的是:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中
Spring IOC实现原理详解之IOC初始化流程
Spring的IOC(控制反转)容器通过管理应用中的对象(即Bean)的生命周期和依赖关系,实现了松耦合、模块化的设计。在Spring中,IOC容器负责创建对象、管理对象之间的依赖关系以及执行生命周期的各个阶段。本文将详细解析Spring IOC的初始化流程,帮助大家深入理解Spring容器的工作原理。
一、Spring IOC容器初始化流程概述
Spring IOC容器初始化过程可简要总结为以下几个步骤:
1. 容器创建与加载配置
2. 读取Bean定义(注册Bean)
3. 实例化Bean对象
4. 填充Bean的依赖(依赖注入)
5. 执行Bean初始化方法
6. 容器准备就绪,提供服务
7. 容器关闭,销毁Bean(如果需要)
二、Spring IOC容器初始化流程详解
Spring容器通过 ApplicationContext 或 BeanFactory 接口来实现,最常见的是 ApplicationContext。创建容器时,容器会从配置文件或注解类中加载Bean定义。
• XML配置方式:通过解析XML文件(如applicationContext.xml),获取Bean的定义。
• 注解方式:通过注解(如@configuration、@componentscan)扫描并注册Bean。
容器的常见实现类:
• ClassPathXmlApplicationContext:用于加载XML配置文件。
• AnnotationConfigApplicationContext:用于Java配置类(通过注解)来配置Spring容器。
例如,使用XML配置的初始化方式如下:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
或者使用Java配置类:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Spring会读取配置文件或注解扫描的类,并解析其中的Bean定义。每个Bean定义会被注册到BeanDefinitionRegistry中。一个BeanDefinition包括Bean的类名、构造函数、依赖关系、作用域等信息。
例如,XML中的Bean定义:
这段XML表示容器中有一个id为myBean,类型为com.example.MyBean的Bean,并且设置了一个属性someProperty的值为someValue。
容器通过 BeanDefinitionReader 类来读取XML配置并将这些Bean定义注册到BeanDefinition容器中。
在Spring容器初始化过程中,容器会根据BeanDefinition来实例化Bean。Spring支持懒加载(lazy loading)和即时加载(eager loading)两种方式:
• 懒加载:只有在第一次访问时,才实例化Bean。
• 即时加载:容器启动时即实例化所有单例Bean。
在实例化Bean时,Spring容器会使用反射机制调用无参构造方法来创建对象。对于需要依赖注入的Bean,Spring会在此阶段记录Bean的依赖关系。
MyBean myBean = new MyBean();
在Bean实例化之后,Spring容器会进行依赖注入(DI,Dependency Injection)。Spring支持以下几种注入方式:
• 构造器注入:通过构造函数将依赖项注入Bean。
• Setter方法注入:通过调用Setter方法来注入依赖。
• 字段注入:通过反射直接设置字段。
Spring会根据BeanDefinition中的依赖关系(如、或注解标记)来确定如何注入Bean的依赖。
例如,XML配置的Setter注入:
构造器注入:
public class MyBean {
private Service service;
}
依赖注入是通过调用Bean的Setter方法或构造方法来实现的。Spring会使用反射机制将依赖注入到Bean实例中。
当所有的依赖注入完成后,Spring会执行Bean的初始化方法。这些初始化方法可以通过以下方式指定:
• @PostConstruct注解:在Bean实例化和依赖注入之后,自动调用初始化方法。
• init-method属性:在XML配置中指定初始化方法。
• InitializingBean接口:实现afterPropertiesSet()方法来自定义初始化逻辑。
例如,使用@PostConstruct注解:
public class MyBean {
}
或者在XML配置中指定:
init-method属性指定的初始化方法将在依赖注入完成之后执行。
在所有的Bean实例化、依赖注入和初始化方法执行完毕后,Spring容器就已经准备好提供服务了。此时可以从容器中获取Bean,并开始使用它们。
MyBean myBean = context.getBean("myBean", MyBean.class);
在这个阶段,Spring容器管理的所有Bean都可以通过容器进行访问,并且可以开始参与业务逻辑的处理。
如果是单例Bean,当容器关闭时,Spring会销毁这些Bean,并执行销毁方法。销毁方法的定义方式有:
• @PreDestroy注解:标注销毁方法。
• destroy-method属性:在XML配置中指定销毁方法。
• DisposableBean接口:实现destroy()方法来自定义销毁逻辑。
例如,使用@PreDestroy注解:
public class MyBean {
}
或者在XML配置中指定销毁方法:
容器关闭时,会根据这些配置调用销毁方法,释放相关资源。
三、总结
Spring IOC初始化流程是一个复杂且精细的过程,涉及了多个阶段的操作,从容器的创建、配置加载、Bean定义的注册、Bean实例化到依赖注入、初始化、以及容器关闭时的销毁过程。每个阶段都离不开Spring容器内部的机制和设计思想,如反射、依赖注入、Bean生命周期管理等。
理解这些初始化流程和背后的实现原理,对于开发者在实际开发中使用Spring框架至关重要。掌握这些细节,能够帮助我们更好地利用Spring容器进行高效的开发和调试,并能够解决更复杂的开发场景中的问题。
四、补充重要知识点
1.首先在初始化容器的时候,调用父类容器AbstractApplicationContext的构造方法(super(parent)方法)为容器设置好Bean资源加载器
通过AbstractApplicationContext默认构造函数初始化容器id, name, 状态 以及 资源解析器
通过AbstractApplicationContext的setParent(parent)方法将父容器的Environment合并到当前容器
在设置容器的资源加载器之后,接下来FileSystemXmlApplicationContet执行setConfigLocations方法通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean定义资源文件的定位
2.初始化的主题流程是从refresh方法开始的,refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器
在refresh方法中,有很多细节,首先 synchronized (this.startupShutdownMonitor)
• 使用 synchronized 锁定 startupShutdownMonitor 对象,以确保在容器的刷新过程中不会有并发调用。
• startupShutdownMonitor 是一个同步对象,用于控制在 Spring 容器的启动和关闭过程中线程的安全性。
这里面有对应的模版方法和钩子方法:
模板方法 是指在父类中定义一个算法的框架,具体的实现由子类来完成。它允许子类在不改变算法结构的情况下实现具体的步骤。
在 Spring 中,refresh() 方法本身就是一个 模板方法,它定义了容器初始化的整体流程,但其中的部分步骤是由子类实现的。refresh() 方法中的调用顺序固定,而一些具体的实现留给子类来执行。
在这段代码中,refresh() 方法按照固定的步骤进行容器的初始化,但一些具体的步骤是由子类(如 AnnotationConfigApplicationContext 或其他具体的 ApplicationContext 实现)来定义和实现。
钩子方法(Hook Method)
钩子方法 是指在父类中定义的方法,允许子类选择性地重写或执行。钩子方法通常是空的或提供默认行为,但子类可以根据需要覆盖它们来扩展父类的功能。
在这段代码中,有几个方法是典型的钩子方法,它们允许子类根据具体需求进行扩展或修改行为:
• onRefresh():
这是一个钩子方法,它在容器刷新时被调用,但默认实现为空。具体的实现逻辑通常由子类提供,用来扩展容器刷新过程中的行为。例如,AnnotationConfigApplicationContext 就会覆盖这个方法来执行特定的初始化逻辑。