背景
Spring的核心是其容器机制,表面看似平静,实则在内部进行着复杂的操作。Springboot进一步封装了Spring,强调“约定优于配置”原则,并采用自动装配机制。通常,引入一个依赖后,我们几乎无需配置即可完成功能的集成。
我特别喜欢这种自动装配机制,并在开发中间件和公共工具时应用它。深入理解 Spring 中bean的生命周期和各扩展接口对于掌握自动装配至关重要,同时也有助于深化对 Spring 的理解,并编写更优雅的业务代码。
本文总结了Spring和Springboot的所有扩展接口及其使用场景,并制作了一个展示bean在Spring内部从加载到初始化的所有可扩展点顺序的图表,帮助理解bean是如何被逐步加载到Spring容器中的。
Bean的生命周期内可扩展点调用顺序图
ApplicationContextInitializer
org.springframework.context.ApplicationContextInitializer
用于在 Spring 应用上下文 (ApplicationContext) 刷新之前对其进行编程式配置。这个接口在 Spring 应用启动过程中的早期阶段被调用,允许开发者在加载任何 bean 之前对应用上下文进行定制。
应用场景
- 定制应用上下文配置:通过实现 ApplicationContextInitializer 接口,可以在 Spring 容器加载任何 bean 定义之前调用自定义逻辑,从而允许对配置进行编程式的修改。在应用启动前检查或准备外部资源,如消息队列
- 环境依赖的设置:它常用于根据不同的环境(如开发、测试、生产)设置不同的配置参数。例如,可以根据不同的配置文件或环境变量来调整数据源设置。如:根据环境变量动态设置数据库连接参数
- 与 Spring Profiles 集成:可以在 ApplicationContextInitializer 中激活或修改 Spring Profiles,从而改变应用程序的行为和配置。
- 动态字节码注入:利用这时候class还没被类加载器加载的时机,进行动态字节码注入等操作
扩展方式
1 | java复制代码public class MyApplicationContextInitializer implements ApplicationContextInitializer { |
生效方式
- 在启动类中用springApplication.addInitializers(new MyApplicationContextInitializer())语句加入
- 配置文件配置context.initializer.classes=com.tf.demo.MyApplicationContextInitializer
- Spring SPI 扩展,在spring.factories中加入org.springframework.context.ApplicationContextInitializer=com.tf.demo.MyApplicationContextInitializer
使用案例
BeanDefinitionRegistryPostProcessor
org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
用于在标准初始化之后和 Bean 实例化之前修改应用程序上下文的 Bean 定义。这个接口扩展了 BeanFactoryPostProcessor,提供了更加灵活的操作 Bean 定义的能力
应用场景
- 修改或增加Bean定义:在 Spring 容器加载了 Bean 定义后,但在实例化 Beans 之前,您可以使用这个接口来修改或添加 Bean 定义。
- 条件性的Bean注册:基于特定条件(比如环境变量或配置参数),动态地注册或修改 Bean。
- 自定义注解处理:如果你需要开发自己的注解并在 Spring 上下文中处理它们,可以使用这个接口。
扩展方式
1 | java复制代码@Component |
生效方式
- 实现接口:您需要创建一个类实现 BeanDefinitionRegistryPostProcessor 接口,并重写 postProcessBeanDefinitionRegistry 和 postProcessBeanFactory 方法。
- 注册到Spring容器:可以通过注解(如 @Component)或在配置类中显式注册。
- 执行顺序:首先执行 postProcessBeanDefinitionRegistry 方法,允许添加或修改 Bean 定义。随后执行 postProcessBeanFactory 方法,这一步通常用于修改已经注册的 Bean 的属性
使用案例
BeanFactoryPostProcessor
org.springframework.beans.factory.config.BeanFactoryPostProcessor
应用场景
- 修改或替换 Bean 定义:如果需要对 Spring 容器中的 Bean 定义进行修改或替换,可以实现此接口。
- 环境检查或配置:在 Spring 容器实例化 beans 之前,进行一些环境的检查或者对配置信息的修改。
- 动态注册 Bean 定义:可以动态地向 Spring 容器添加新的 Bean 定义
扩展方式
1 | java复制代码public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor { |
使用案例
1 | java复制代码public class DataSourceBeanFactoryPostProcessor implements BeanFactoryPostProcessor { |
InstantiationAwareBeanPostProcessor
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor 继承 BeanPostProcess ,BeanPostProcess 接口只在bean的初始化阶段进行扩展(注入spring上下文前后),而InstantiationAwareBeanPostProcessor 接口在此基础上增加了3个方法,把可扩展的范围增加了实例化阶段和属性注入阶段。
应用场景
该类主要的扩展点有以下 5 个方法,主要在 bean 生命周期的两大阶段:实例化阶段和初始化阶段 ,下面一起进行说明,按调用顺序为:
- postProcessBeforeInstantiation:实例化 bean 之前,相当于 new 这个 bean 之前,可用于修改动态环境 RocketMQ 消费者组名称,以及一些中间件 bean 属性的动态修改
- postProcessAfterInstantiation:实例化 bean 之后,相当于 new 这个 bean 之后
- postProcessPropertyValues:bean 已经实例化完成,在属性注入时阶段触发,@Autowired、@Resource等注解原理基于此方法实现
- postProcessBeforeInitialization:初始化 bean 之前,相当于把 bean 注入 spring 上下文之前
- postProcessAfterInitialization:初始化 bean 之后,相当于把 bean 注入 spring 上下文之后
扩展方式
1 | java复制代码import org.springframework.beans.BeansException; |
使用案例
1 | java复制代码@Slf4j |
SmartInstantiationAwareBeanPostProcessor
org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor
应用场景
SmartInstantiationAwareBeanPostProcessor 是 Spring 框架中一个更高级的扩展接口,提供了更细粒度的控制来干预和修改 Bean 的实例化过程。这个接口扩展了 InstantiationAwareBeanPostProcessor,增加了三个主要的方法,分别对应于 Bean 生命周期的不同阶段。以下是对这三个方法的优化建议:
- predictBeanType: 此方法主要用于预测 Bean 的类型。当通过 Bean 名称无法确定其类型时,此方法被调用。它在实际 Bean 实例化之前提供了一种机制来推断 Bean 的类型。
- determineCandidateConstructors:目的与应用场景: 该方法用于确定 Bean 的构造函数。这对于自定义选择合适的构造器来实例化 Bean 特别有用,尤其是在有多个构造函数的情况下。
- getEarlyBeanReference:目的与应用场景: 该方法在 Bean 实例化后、初始化前被调用,主要用于解决循环依赖的问题,它允许在完全初始化之前提供 Bean 的早期引用。
扩展方式
1 | java复制代码import org.springframework.beans.BeansException; |
使用案例
BeanFactoryAware
org.springframework.beans.factory.BeanFactoryAware
应用场景
该类具有一个关键时机点,即在 Bean 实例化之后且属性注入(例如,通过 Setter 方法)之前。此时,通过重写 setBeanFactory 方法,类可以获得 BeanFactory 实例的引用。
在这个阶段,您可以对刚实例化但尚未完全初始化的 Bean 进行特殊处理。这为在 Bean 生命周期的早期阶段进行定制化操作提供了机会。此外,您也可以将 BeanFactory 实例缓存起来,以便于后续操作时重复使用,从而提高效率和灵活性。这种做法特别适用于需要根据运行时条件动态处理或检索 Bean 的场景
扩展方式
1 | java复制代码import org.springframework.beans.BeansException; |
ApplicationContextAwareProcessor
org.springframework.context.support.ApplicationContextAwareProcessor
应用场景
针对您提到的ApplicationContextAwareProcessor类及其相关的六个扩展点,可以进行如下优化和概述:
- EnvironmentAware:这个接口允许Bean获取到Spring环境相关的配置。虽然通常可以通过注入的方式直接获得环境参数,但实现这个接口可以在Bean中直接访问环境属性,有助于减少对Spring环境的直接依赖。
- EmbeddedValueResolverAware:实现此接口使得Bean能够解析String类型的属性值。虽然@Value注解通常被用于注入属性值,但通过缓存StringValueResolver实例,可以在需要时获取这些值,提供更灵活的值解析方式。
- ResourceLoaderAware:通过这个接口,Bean可以获得ResourceLoader,用于访问类路径内的资源。这使得Bean能够更灵活地处理外部资源,例如配置文件或类路径资源。
- ApplicationEventPublisherAware:实现这个接口使Bean能够发布事件。虽然可以通过Spring注入来获取ApplicationEventPublisher,但直接在Bean中实现此接口可以简化事件发布流程,增强Bean的事件驱动能力。
- MessageSourceAware:这个接口主要用于国际化支持,允许Bean访问MessageSource。这对于开发需要支持多语言的应用程序尤其重要。
- ApplicationContextAware:通过实现这个接口,Bean可以直接访问Spring的应用程序上下文ApplicationContext。这使得Bean能够获取和操作其他Bean,以及使用上下文提供的其他功能。这对于需要进行复杂上下文操作的Bean尤为重要
扩展方式
1 | java复制代码import org.springframework.context.*; |
BeanNameAware
org.springframework.beans.factory.BeanNameAware
应用场景
BeanNameAware的主要应用场景包括:
- 日志记录:Bean可以在日志中记录自己的名字,这对于调试和跟踪Bean的创建及其在容器中的生命周期是非常有用的。
- 依赖注入:在某些复杂的依赖注入场景中,Bean可能需要知道自己的名字来动态地处理依赖关系,尤其是在存在多个相同类型但需要不同处理的Bean时。
- 上下文感知:对于需要根据其在容器中的角色或标识来改变行为的Bean,了解自己的名字是很重要的。例如,同一个类的不同实例可能需要根据其在Spring容器中的名字来加载不同的配置。
- 与其他框架集成:在集成Spring与其他框架(如Quartz, Apache Camel等)时,知道Bean的名字可以帮助在框架之间传递信息,确保正确的配置和交互。
- 测试和模拟:在测试环境中,可以利用Bean的名字进行模拟或者特定的测试设置。
扩展方式
1 | java复制代码import org.springframework.beans.factory.BeanNameAware; |
@PostConstruct
javax.annotation.PostConstruct
应用场景
- 资源初始化:在数据库连接、读取配置文件或者初始化一些数据结构时使用。
- 日志记录:应用启动时,记录一些启动日志或者系统状态。
- 数据预加载:预加载一些必要的数据到缓存中。
- 检查依赖:确保应用的某些依赖项已经准备就绪。
扩展方式
1 | java复制代码import javax.annotation.PostConstruct; |
InitializingBean
org.springframework.beans.factory.InitializingBean
应用场景
InitializingBean接口用于在设置完一个bean的所有属性之后执行初始化工作。实现InitializingBean接口允许bean在其属性被设置之后,但在任何启动逻辑(如数据库连接)之前执行自定义初始化逻辑。
- 资源初始化:如数据库连接、文件系统的访问或其他需要在属性设置后进行初始化的资源。
- 自定义配置校验:在bean的属性被注入后,检查或校验这些属性。
- 复杂的初始化逻辑:执行复杂的初始化逻辑,这些逻辑可能依赖于多个属性
扩展方式
1 | java复制代码import org.springframework.beans.factory.InitializingBean; |
注意:如果同时使用了InitializingBean接口和@PostConstruct注解,那么@PostConstruct注解的方法将在afterPropertiesSet()方法之前执行
FactoryBean
org.springframework.beans.factory.FactoryBean
应用场景
FactoryBean是一个特殊的bean类型,用于创建复杂对象。当你需要执行复杂的初始化逻辑,或者创建对象过程中需要多个步骤和配置时,使用FactoryBean可以是一个好选择。它允许你在Spring容器内部完全控制bean的创建过程。
- 复杂对象的创建:当一个对象的创建过程非常复杂,例如需要多步构建过程或特定的配置逻辑时。
- 依赖注入之外的控制:如果需要在标准依赖注入之外控制对象的创建逻辑。
- 单例与非单例的管理:可以灵活地创建单例或原型(非单例)对象。
- 创建特定类型的资源:例如连接到特定服务的代理或特殊类型的资源。
扩展方式
1 | java复制代码import org.springframework.beans.factory.FactoryBean; |
SmartInitializingSingleton
org.springframework.beans.factory.SmartInitializingSingleton
应用场景
SmartInitializingSingleton接口主要用于在单例bean的全部依赖关系都被满足后执行特定的逻辑。这个接口通常用于在Spring容器的启动阶段的最后一步,所有单例bean都已经创建且依赖关系已经注入后,执行一些后处理操作。
- 后处理逻辑:在所有单例bean初始化完成之后执行一些后处理操作,例如数据校验、缓存预热等。
- 依赖于多个Bean的初始化:当你的逻辑需要确保多个其他bean已经初始化完成时使用。
- 异步操作的启动:如启动异步任务,当所有必要的bean都已经准备好。
- 复杂的启动流程:例如,当应用需要在启动时执行一系列复杂的初始化步骤
扩展方式
1 | java复制代码import org.springframework.beans.factory.SmartInitializingSingleton; |
CommandLineRunner
org.springframework.boot.CommandLineRunner
应用场景
CommandLineRunner接口用于在Spring应用启动后执行特定的代码。它提供了一种简单的方法来访问命令行参数,并在Spring应用上下文加载完成后立即执行一些操作。
- 命令行参数处理:解析和处理传递给Spring应用的命令行参数。
- 启动时数据初始化:在应用启动时加载数据,例如从文件读取数据或者数据库初始化。
- 启动后的检查:进行一些启动后的健康检查或者环境检查。
- 启动任务:如启动一个定时任务或触发一些仅需执行一次的操作。
扩展方式
1 | java复制代码import org.springframework.boot.CommandLineRunner; |
DisposableBean
org.springframework.beans.factory.DisposableBean
应用场景
DisposableBean接口用于在bean的生命周期结束时执行清理工作,比如释放资源或执行其他清理操作。这通常用于在bean不再需要时,确保以优雅的方式释放资源,如关闭数据库连接、释放文件句柄等。
- 资源释放:关闭打开的资源,例如文件流、数据库连接、网络连接等。
- 停止后台线程:如果bean启动了任何后台线程,可以在销毁时停止这些线程。
- 缓存清理:清理任何持久化的缓存或临时数据。
- 注销注册:如果bean在系统或服务中进行了注册,可以在销毁时注销
扩展方式
1 | java复制代码import org.springframework.beans.factory.DisposableBean; |
ApplicationListener
org.springframework.context.ApplicationListener
应用场景
ApplicationListener接口用于实现事件监听机制。这个接口允许应用对各种应用事件(如上下文事件、请求处理事件等)作出响应。它是基于观察者设计模式的实现,允许你在特定事件发生时执行自定义的逻辑。
- 上下文事件监听:监听如上下文初始化、关闭等事件。
- 自定义事件处理:处理自定义定义的业务事件。
- 监控与日志:对特定事件进行监控或记录日志。
- 性能跟踪:追踪应用的关键操作,如请求的处理时间。
扩展方式
1 | java复制代码import org.springframework.context.ApplicationListener; |
本文转载自: 掘金