千家信息网

SpringBoot Bean中如何指定初始化顺序的若干姿势

发表于:2025-12-03 作者:千家信息网编辑
千家信息网最后更新 2025年12月03日,SpringBoot Bean中如何指定初始化顺序的若干姿势,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。I. 环境搭建
千家信息网最后更新 2025年12月03日SpringBoot Bean中如何指定初始化顺序的若干姿势

SpringBoot Bean中如何指定初始化顺序的若干姿势,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

I. 环境搭建

我们的测试项目和上一篇博文公用一个项目环境,当然也可以建一个全新的测试项目,对应的配置如下:(文末有源码地址)

    org.springframework.boot    spring-boot-starter-parent    2.1.7         UTF-8    UTF-8    Finchley.RELEASE    1.8            org.springframework.boot        spring-boot-starter-web                                            org.springframework.boot                spring-boot-maven-plugin                                    spring-milestones        Spring Milestones        https://repo.spring.io/milestone                    false            

II. 初始化顺序指定

1. 构造方法依赖

这种可以说是最简单也是最常见的使用姿势,但是在使用时,需要注意循环依赖等问题

我们知道 bean 的注入方式之中,有一个就是通过构造方法来注入,借助这种方式,我们可以解决有优先级要求的 bean 之间的初始化顺序

比如我们创建两个 Bean,要求 CDemo2 在 CDemo1 之前被初始化,那么我们的可用方式

@Componentpublic class CDemo1 {    private String name = "cdemo 1";    public CDemo1(CDemo2 cDemo2) {        System.out.println(name);    }}@Componentpublic class CDemo2 {    private String name = "cdemo 1";    public CDemo2() {        System.out.println(name);    }}

实测输出结果如下,和我们预期一致

虽然这种方式比较直观简单,但是有几个限制

  • 需要有注入关系,如 CDemo2 通过构造方法注入到 CDemo1 中,如果需要指定两个没有注入关系的 bean 之间优先级,则不太合适(比如我希望某个 bean 在所有其他的 Bean 初始化之前执行)

  • 循环依赖问题,如过上面的 CDemo2 的构造方法有一个 CDemo1 参数,那么循环依赖产生,应用无法启动

另外一个需要注意的点是,在构造方法中,不应有复杂耗时的逻辑,会拖慢应用的启动时间

2. @DependOn 注解

这是一个专用于解决 bean 的依赖问题,当一个 bean 需要在另一个 bean 初始化之后再初始化时,可以使用这个注解

使用方式也比较简单了,下面是一个简单的实例 case

@DependsOn("rightDemo2")@Componentpublic class RightDemo1 {    private String name = "right demo 1";    public RightDemo1() {        System.out.println(name);    }}@Componentpublic class RightDemo2 {    private String name = "right demo 2";    public RightDemo2() {        System.out.println(name);    }}

上面的注解放在 RightDemo1 上,表示RightDemo1的初始化依赖于rightDemo2这个 bean

在使用这个注解的时候,有一点需要特别注意,它能控制 bean 的实例化顺序,但是 bean 的初始化操作(如构造 bean 实例之后,调用@PostConstruct注解的初始化方法)顺序则不能保证,比如我们下面的一个实例,可以说明这个问题

@DependsOn("rightDemo2")@Componentpublic class RightDemo1 {    private String name = "right demo 1";    @Autowired    private RightDemo2 rightDemo2;    public RightDemo1() {        System.out.println(name);    }    @PostConstruct    public void init() {        System.out.println(name + " _init");    }}@Componentpublic class RightDemo2 {    private String name = "right demo 2";    @Autowired    private RightDemo1 rightDemo1;    public RightDemo2() {        System.out.println(name);    }    @PostConstruct    public void init() {        System.out.println(name + " _init");    }}

注意上面的代码,虽然说有循环依赖,但是通过@Autowired注解方式注入的,所以不会导致应用启动失败,我们先看一下输出结果

有意思的地方来了,我们通过@DependsOn注解来确保在创建RightDemo1之前,先得创建RightDemo2

所以从构造方法的输出可以知道,先实例 RightDemo2, 然后实例 RightDemo1;

然后从初始化方法的输出可以知道,在上面这个场景中,虽然 RightDemo2 这个 bean 创建了,但是它的初始化代码在后面执行

题外话: 有兴趣的同学可以试一下把上面测试代码中的@Autowired的依赖注入删除,即两个 bean 没有相互注入依赖,再执行时,会发现输出顺序又不一样

3. BeanPostProcessor

最后再介绍一种非典型的使用方式,如非必要,请不要用这种方式来控制 bean 的加载顺序

先创建两个测试 bean

@Componentpublic class HDemo1 {    private String name = "h demo 1";    public HDemo1() {        System.out.println(name);    }}@Componentpublic class HDemo2 {    private String name = "h demo 2";    public HDemo2() {        System.out.println(name);    }}

我们希望 HDemo2 在 HDemo1 之前被加载,借助 BeanPostProcessor,我们可以按照下面的方式来实现

@Componentpublic class DemoBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements BeanFactoryAware {    private ConfigurableListableBeanFactory beanFactory;    @Override    public void setBeanFactory(BeanFactory beanFactory) {        if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {            throw new IllegalArgumentException(                    "AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);        }        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;    }    @Override    @Nullable    public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {        // 在bean实例化之前做某些操作        if ("HDemo1".equals(beanName)) {            HDemo2 demo2 = beanFactory.getBean(HDemo2.class);        }        return null;    }}

请将目标集中在postProcessBeforeInstantiation,这个方法在某个 bean 的实例化之前,会被调用,这就给了我们控制 bean 加载顺序的机会

看到这种骚操作,是不是有点蠢蠢欲动,比如我有个 bean,希望在应用启动之后,其他的 bean 实例化之前就被加载,用这种方式是不是也可以实现呢?

下面是一个简单的实例 demo,重写DemoBeanPostProcessorpostProcessAfterInstantiation方法,在 application 创建之后,就加载我们的 FDemo 这个 bean

@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {    if ("application".equals(beanName)) {        beanFactory.getBean(FDemo.class);    }    return true;}@DependsOn("HDemo")@Componentpublic class FDemo {    private String name = "F demo";    public FDemo() {        System.out.println(name);    }}@Componentpublic class HDemo {    private String name = "H demo";    public HDemo() {        System.out.println(name);    }}

从下图输出可以看出,HDemo, FDemo的实例化顺序放在了最前面了

4. 小结

在小结之前,先指出一下,一个完整的 bean 创建,在本文中区分了两块顺序

  • 实例化 (调用构造方法)

  • 初始化 (注入依赖属性,调用@PostConstruct方法)

主要介绍了三种方式来控制 bean 的加载顺序,分别是

  • 通过构造方法依赖的方式,来控制有依赖关系的 bean 之间初始化顺序,但是需要注意循环依赖的问题

  • @DependsOn注解,来控制 bean 之间的实例顺序,需要注意的是 bean 的初始化方法调用顺序无法保证

  • BeanPostProcessor 方式,来手动控制 bean 的加载顺序

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注行业资讯频道,感谢您对的支持。

顺序 方法 实例 方式 注解 控制 输出 问题 面的 循环 两个 之间 应用 测试 代码 项目 姿势 优先级 小结 环境 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全整改问题清单 修改学校服务器成绩 appserv 数据库名 深圳c 软件开发招聘 方舟一个服务器多少人 对软件开发文档有什么要求 北京太极网络技术有限公司 ATM宽带网络技术应用 南宁良庆区软件开发地址 神通数据库配置工具打不开 当前网络技术应用的看法与分析 2016网络安全案例分析 万方数据资源属于事实数据库吗 腾讯云服务器怎么设置成公众号 我的世界游戏服务器崩溃 软管理服务器被破坏怎么办 传奇服务器哪款好 软件开发项目工程管理 课堂网络安全标语图片 王牌战争新服官方服务器开启时间 武汉大学国家网络安全学院程媛 深圳网络技术有限公司笔试题目 数据库引用工作表的工作编号 自建音乐服务器的价格 小学网络安全教育班队会纪实 带100台电脑服务器配置 无锡参考软件开发系统 数据库转移技术 长春智能化网络技术咨询哪家好 网络安全法诞生视屏
0