前言
正常情况下,Spring 容器加载 Bean 的顺序是不确定的,那么我们如果需要按顺序加载 Bean 时应如何操作?本文将详细讲述我们如何才能控制 Bean 的加载顺序。
场景
我创建了 4 个 Class 文件,分别命名为
-
FirstInitialization
-
SecondInitialization
-
ThirdInitialization
-
ForthInitialization
我希望这 4 个类按照 1、2、3、4 的顺序加载。
如下图,直接加载的话,顺序是 1、4、2、3,并不能达到要求。
如何控制
注意:网上很多文章说
Order
注解或Ordered
接口可以控制 Bean 的加载顺序,其是并不能,它们的作用是定义 Spring IOC 容器中 Bean 定义类的执行顺序的优先级,并不是定义加载顺序。
使用@DependsOn 注解
在需要调整顺序的类上依次加@DependsOn
注解,缺点是类过多的时候需要一个个加注解,且不好维护。
@Component
public class FirstInitialization {@PostConstructpublic void init(){System.out.println("我是第一个加载!");}}
@Component
@DependsOn("firstInitialization")
public class SecondInitialization {@PostConstructpublic void init(){System.out.println("我是第二个加载!");}}
@Component
@DependsOn("secondInitialization")
public class ThirdInitialization {@PostConstructpublic void init(){System.out.println("我是第三个加载!");}}
@Component
@DependsOn("thirdInitialization")
public class ForthInitialization {@PostConstructpublic void init(){System.out.println("我是第四个加载!");}}
执行结果如下
基于 ApplicationContextInitializer 接口
接口简介
这里我简单介绍一个这个接口的用处, 等到整理到相关源码的时候再详细介绍。
ApplicationContextInitializer
接口是在 Spring 容器刷新之前执行的一个回调函数。
执行时机:
Spring 内部执行
ConfigurableApplicationContext#refresh()
方法前;SpringBoot 执行
run()
方法前。
一般有什么用呢?
在 SpringBoot 应用中 Classpath 上会有很多 jar 包,有些 jar 包需要在refresh()
调用前对应用上下文做一些初始化动作,因此会提供ApplicationContextInitializer
接口的实现类,放在如下图的文件中,这样会被SpringApplication#initialize
发现,然后完成对应初始化。
实现步骤
首先创建一个类继承ApplicationContextInitializer
接口。
public class MyApplicationContextInitializer implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {//将自定义的BeanFactoryPostProcessor实现类保存到ApplicationContext中applicationContext.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());}
}
创建`META-INF/spring.factories`文件。
自定义`BeanDefinitionRegistryPostProcessor`。
/*** BeanFactoryPostProcessor的子类* 允许开发人员在Bean定义注册之前和之后对BeanDefinition进行自定义处理,例如添加,修改或删除Bean定义等。*/
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {// 初始化需要排序的类,这里要保证插入顺序只能用LinkedHashMapprivate static final Map<String, Class> ORDER_BEAN_MAP = new LinkedHashMap<>() {{put("firstInitialization", FirstInitialization.class);put("secondInitialization", SecondInitialization.class);put("thirdInitialization", ThirdInitialization.class);put("forthInitialization", ForthInitialization.class);}};@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {Optional.ofNullable(ORDER_BEAN_MAP.keySet()).orElse(new HashSet<>()).stream().forEach(beanName -> {// 初始化一个 Bean 定义AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();// 按顺序注册每个BeanbeanDefinition.setBeanClass(ORDER_BEAN_MAP.get(beanName));registry.registerBeanDefinition(beanName, beanDefinition);});}
}
执行结果如下
文章转载自:fuxing.
原文链接:https://www.cnblogs.com/fuxing/p/18181623
体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构