来自云龙湖轮廓分明的月亮 发表于 2024-5-19 13:05:55

Spring怎样控制Bean的加载顺序

前言

正常情况下,Spring 容器加载 Bean 的顺序是不确定的,那么我们如果必要按顺序加载 Bean 时应怎样操纵?本文将详细报告我们怎样才气控制 Bean 的加载顺序。


场景

我创建了 4 个 Class 文件,分别命名为

[*]FirstInitialization
[*]SecondInitialization
[*]ThirdInitialization
[*]ForthInitialization
我盼望这 4 个类按照 1、2、3、4 的顺序加载。


如下图,直接加载的话,顺序是 1、4、2、3,并不能达到要求。
https://img2024.cnblogs.com/blog/2358990/202405/2358990-20240509103945106-509633035.png
怎样控制


注意:网上很多文章说Order注解或Ordered接口可以控制 Bean 的加载顺序,其是并不能,它们的作用是定义 Spring IOC 容器中 Bean 定义类的执行顺序的优先级,并不是定义加载顺序。

利用@DependsOn 注解

在必要调整顺序的类上依次加@DependsOn注解,缺点是类过多的时间必要一个个加注解,且不好维护。


@Component
public class FirstInitialization {

    @PostConstruct
    public void init(){
      System.out.println("我是第一个加载!");
    }

}@Component
@DependsOn("firstInitialization")
public class SecondInitialization {

    @PostConstruct
    public void init(){
      System.out.println("我是第二个加载!");
    }

}@Component
@DependsOn("secondInitialization")
public class ThirdInitialization {

    @PostConstruct
    public void init(){
      System.out.println("我是第三个加载!");
    }

}@Component
@DependsOn("thirdInitialization")
public class ForthInitialization {

    @PostConstruct
    public void init(){
      System.out.println("我是第四个加载!");
    }

}
执行结果如下https://img2024.cnblogs.com/blog/2358990/202405/2358990-20240509103958573-2045805728.png
基于 ApplicationContextInitializer 接口


接口简介

这里我简单介绍一个这个接口的用处, 比及整理到相关源码的时间再详细介绍。


ApplicationContextInitializer接口是在 Spring 容器刷新之前执行的一个回调函数。


执行机遇:

[*]Spring 内部执行ConfigurableApplicationContext#refresh()方法前;
[*]SpringBoot 执行run()方法前。


一样平常有什么用呢?
在 SpringBoot 应用中 Classpath 上会有很多 jar 包,有些 jar 包必要在refresh()调用前对应用上下文做一些初始化动作,因此会提供ApplicationContextInitializer接口的实现类,放在如下图的文件中,这样会被SpringApplication#initialize发现,然后完成对应初始化。


https://img2024.cnblogs.com/blog/2358990/202405/2358990-20240509104028948-88072235.png
实现步调


首先创建一个类继承ApplicationContextInitializer接口。


public class MyApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {

      //将自定义的BeanFactoryPostProcessor实现类保存到ApplicationContext中
      applicationContext.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());
    }
}
创建`META-INF/spring.factories`文件。https://img2024.cnblogs.com/blog/2358990/202405/2358990-20240509104040129-742568128.png

自定义`BeanDefinitionRegistryPostProcessor`。/**
* BeanFactoryPostProcessor的子类
* 允许开发人员在Bean定义注册之前和之后对BeanDefinition进行自定义处理,例如添加,修改或删除Bean定义等。
*/
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    // 初始化需要排序的类,这里要保证插入顺序只能用LinkedHashMap
    private 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);
      }
    };

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
      Optional.ofNullable(ORDER_BEAN_MAP.keySet()).orElse(new HashSet<>()).stream()
                .forEach(beanName -> {
                  // 初始化一个 Bean 定义
                  AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition().getBeanDefinition();

                  // 按顺序注册每个Bean
                  beanDefinition.setBeanClass(ORDER_BEAN_MAP.get(beanName));
                  registry.registerBeanDefinition(beanName, beanDefinition);
                });
    }
}
执行结果如下https://img2024.cnblogs.com/blog/2358990/202405/2358990-20240509103958573-2045805728.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Spring怎样控制Bean的加载顺序