写点什么

Spring 如何控制 Bean 的加载顺序

  • 2024-05-10
    福建
  • 本文字数:1953 字

    阅读完需:约 6 分钟

前言


正常情况下,Spring 容器加载 Bean 的顺序是不确定的,那么我们如果需要按顺序加载 Bean 时应如何操作?本文将详细讲述我们如何才能控制 Bean 的加载顺序。


场景


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

  1. FirstInitialization

  2. SecondInitialization

  3. ThirdInitialization

  4. ForthInitialization


我希望这 4 个类按照 1、2、3、4 的顺序加载。


如下图,直接加载的话,顺序是 1、4、2、3,并不能达到要求。



如何控制


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


使用 @DependsOn 注解


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


@Componentpublic 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("我是第四个加载!");    } }
复制代码


执行结果如下



基于 ApplicationContextInitializer 接口


接口简介


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


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


执行时机:

  1. Spring 内部执行ConfigurableApplicationContext#refresh()方法前;

  2. SpringBoot 执行run()方法前。


一般有什么用呢?


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



实现步骤


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

public class MyApplicationContextInitializer implements ApplicationContextInitializer {    @Override    public void initialize(ConfigurableApplicationContext applicationContext) {         //将自定义的BeanFactoryPostProcessor实现类保存到ApplicationContext中        applicationContext.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());    }}
复制代码


创建`META-INF/spring.factories`文件。



自定义`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);                });    }}
复制代码


执行结果如下



文章转载自:fuxing.

原文链接:https://www.cnblogs.com/fuxing/p/18181623

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
Spring如何控制Bean的加载顺序_Java_快乐非自愿限量之名_InfoQ写作社区