前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实战指南:四种调整 Spring Bean 初始化顺序的方案

实战指南:四种调整 Spring Bean 初始化顺序的方案

作者头像
每周聚焦
发布2024-11-11 17:21:11
980
发布2024-11-11 17:21:11

背景

因为业务需求,mentor想要某些 bean 启动时优先加载,将数据存入缓存,便问我,“能不能调下Bean初始化顺序?”,于是便有了这篇文章

结构演示

image.png
image.png

目前一共有两个 service ,每个 service 都有一个 init 方法,打印bean创建时机,正常状态打印结果如下:

image.png
image.png

正文

方案一 ( @Order )

这是第一个想到的方法,我们给每个service上加上@Order,让他们倒序创建

代码

image.png
image.png

结果

image.png
image.png

嗯?不是数字越低优先级越高吗,结果怎么还是 1 -> 2?相信眼尖的人已经看出来了,我在开头埋了个坑,用 @PostConstruct 作初始化操作

题外话( @PostConstruct 和 @Order 优先级)

  • @PostConstruct 修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次,也就在依赖注入完成后立即调用,bean 初始化阶段执行的。
  • @Order 注解用于设置组件的执行顺序,排序集合或指定某些类型的组件的优先级

@PostConstruct 方法的执行顺序是由 Spring 容器在 bean 初始化过程中自动管理的,与 @Order 注解无关。

SpringBootrun 源码角度来看

image.png
image.png

结论

优点 :简单明了,适用于需要简单控制初始化顺序的场景。

缺点:只适用于具有顺序的Bean,无法处理复杂依赖关系,遇到如 @PostConstruct 会失效

方案二 ( SmartInitializingSingleton )

SmartInitializingSingleton 用于在容器完成所有单例 bean 的初始化后执行一些额外的初始化工作。用这个接口应该能保证 FirstService 后创建了吧

代码

image.png
image.png

结果

image.png
image.png

还是不行@PostConstruct 的东西还是先创建了,不过起码保证了 FirstServiceafterSingletonsInstantiated 方法是所有单例 Bean 初始化之后执行的。

不过毫无疑问,被驳回了,还说这要是有三个或多个 Bean 有这业务怎么办

结论

优点 :确保所有单例bean都初始化,适合在所有Bean创建后执行全局初始化逻辑。

缺点:不适合控制特定Bean之间的初始化顺序。

方案三 ( @DependsOn )

既然加载顺序不行,还要有多个 Bean ,那就从 Bean 间的依赖入手嘛

代码

image.png
image.png

结果

image.png
image.png

不出意外的成功了,但是mentor嫌耦合性太高,一处改了处处改,后期项目大了找不到不方便维护

结论

优点 :简单明了,适用于明确的依赖关系。

缺点:依赖关系硬编码在配置类中,灵活性较低,耦合性高。

方案四 ( 自定义 Bean 初始化类 )

那好嘛,那自定义呗

代码

image.png
image.png

在 META-INF 的 spring.factories 加上配置

代码语言:javascript
复制
ini 代码解读复制代码org.springframework.context.ApplicationContextInitializer=\com.hhh.init.MyBeanInit

解释

  1. 该自定义类 MyBeanInit 分别继承了 BeanDefinitionRegistryPostProcessorApplicationContextInitializer ,重写其内部方法
    • BeanDefinitionRegistryPostProcessor提前注册或修改 Bean 定义,在所有其他 BeanFactoryPostProcessor 运行之前执行,允许我们注册或修改 BeanDefinition
    • ApplicationContextInitializer初始化应用上下文,在应用上下文刷新之前调用,可以动态地为应用上下文添加属性、BeanFactoryPostProcessor 或其他配置。
  2. postProcessBeanDefinitionRegistry 方法中,使用 BeanDefinitionBuilder 创建了两个 AbstractBeanDefinition 实例,分别对应 ThirdServiceSecondService 类,然后,将这些 Bean 定义注册到 Spring 容器中,分别命名为 "thirdService""secondService"
  3. initialize 方法中,将当前的 BeanDefinitionRegistryPostProcessor 实例( MyBeanInit 自身)添加到应用上下文的 BeanFactoryPostProcessor 列表中。在容器启动时,postProcessBeanDefinitionRegistry 方法将被调用,从而注册我们在上面定义的 Bean。

简而言之:自定义 Bean 注册方法,将自己想要优先加载的 Bean 塞进去,再加入上下文中加载

这里我们也可以把 @Component 去掉,因为在自定义初始化类中加载了,不需要被 ComponentScan 再扫描注册一次,以免出现重复注册异常

image.png
image.png

结果

image.png
image.png

结论

优点 :灵活性高,可以用于复杂的初始化逻辑。

缺点 :需要手动管理Bean的初始化顺序,代码维护成本较高。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 结构演示
  • 正文
    • 方案一 ( @Order )
      • 题外话( @PostConstruct 和 @Order 优先级)
      • 结论
    • 方案二 ( SmartInitializingSingleton )
      • 结论
    • 方案三 ( @DependsOn )
      • 结论
    • 方案四 ( 自定义 Bean 初始化类 )
      • 结论
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档