• Spring如何控制Bean的加载顺序


    前言

    正常情况下,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注解,缺点是类过多的时候需要一个个加注解,且不好维护

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

    执行结果如下

    基于 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 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);
                    });
        }
    }
    

    执行结果如下

  • 相关阅读:
    SQL Server 备份与恢复
    openstack搭建
    微信小程序链接快速生成方法
    Debian11安装Proxmox VE 7
    Spring Cloud Gateway整合Nacos实现服务路由及集群负载均衡
    PVE Cloud-INIT 模板配置
    Yolov8部署——vs2019遇到的问题
    富格林:掌握鉴别阻挠虚假套路
    C++项目实战——基于多设计模式下的同步&异步日志系统-⑨-同步日志器类与日志器建造者类设计
    RHCE---作业3
  • 原文地址:https://www.cnblogs.com/fuxing/p/18181623