Spring(SpringBoot)--控制Bean注入的顺序

打印 上一主题 下一主题

主题 691|帖子 691|积分 2073

原文网址:Spring(SpringBoot)--控制Bean注入的顺序_IT利刃出鞘的博客-CSDN博客 ​
简介

说明
        本文介绍Spring如何控制Bean注入的顺序。
        首先需要说明的是:在Bean上加@Order(xxx)是无法控制bean注入的顺序的!
控制bean的加载顺序的方法

  •  构造方法依赖
  • @DependsOn 注解
  • BeanPostProcessor 扩展 
         Bean初始化顺序与类加载顺序基本一致:静态变量/语句块=> 实例变量或初始化语句块=> 构造方法=> @Autowirebean注入的顺序
1. 构造方法依赖(推荐)

创建两个Bean,要求CDemo2在CDemo1之前被初始化。
  1. @Component
  2. public class CDemo1 {
  3.     private String name = "cdemo 1";
  4.     public CDemo1(CDemo2 cDemo2) {
  5.         System.out.println(name);
  6.     }
  7. }
  8. @Component
  9. public class CDemo2 {
  10.     private String name = "cdemo 2";
  11.     public CDemo2() {
  12.         System.out.println(name);
  13.     }
  14. }
复制代码
结果(和预期一致)


限制


  • 要有注入关系,如:CDemo2通过构造方法注入到CDemo1中,若需要指定两个没有注入关系的bean之间优先级,则不太合适(比如我希望某个bean在所有其他的Bean初始化之前执行)
  • 循环依赖问题,如过上面的CDemo2的构造方法有一个CDemo1参数,那么循环依赖产生,应用无法启动
另外一个需要注意的点是,在构造方法中,不应有复杂耗时的逻辑,会拖慢应用的启动时间
2. @DependsOn(不推荐)

        不推荐的原因:这种方法是通过bean的名字(字符串)来控制顺序的,如果改了bean的类名,很可能就会忘记来改所有用到它的注解,那就问题大了。
当一个bean需要在另一个bean实例化之后再实例化时,可使用这个注解。
  1. @DependsOn("rightDemo2")
  2. @Component
  3. public class RightDemo1 {
  4.     private String name = "right demo 1";
  5.     public RightDemo1() {
  6.         System.out.println(name);
  7.     }
  8. }
  9. @Component
  10. public class RightDemo2 {
  11.     private String name = "right demo 2";
  12.     public RightDemo2() {
  13.         System.out.println(name);
  14.     }
  15. }
复制代码
上面的注解放在 RightDemo1 上,表示RightDemo1的初始化依赖于rightDemo2这个bean

        它能控制bean的实例化顺序,但是bean的初始化操作(如构造bean实例之后,调用@PostConstruct注解的初始化方法)顺序则不能保证,比如我们下面的一个实例,可以说明这个问题
  1. @DependsOn("rightDemo2")
  2. @Component
  3. public class RightDemo1 {
  4.     private String name = "right demo 1";
  5.     @Autowired
  6.     private RightDemo2 rightDemo2;
  7.     public RightDemo1() {
  8.         System.out.println(name);
  9.     }
  10.     @PostConstruct
  11.     public void init() {
  12.         System.out.println(name + " _init");
  13.     }
  14. }
  15. @Component
  16. public class RightDemo2 {
  17.     private String name = "right demo 2";
  18.     @Autowired
  19.     private RightDemo1 rightDemo1;
  20.     public RightDemo2() {
  21.         System.out.println(name);
  22.     }
  23.     @PostConstruct
  24.     public void init() {
  25.         System.out.println(name + " _init");
  26.     }
  27. }
复制代码
结果(先实例的Bean反而在后边执行init)

 把上面测试代码中的@Autowired的依赖注入删除,即两个bean没有相互注入依赖,再执行,会发现输出顺序又不一样
3. BeanPostProcessor(不推荐)

一种非典型的使用方式,如非必要,请不要用这种方式来控制bean的加载顺序。
场景1:希望HDemo2在HDemo1之前被加载 
  1. @Component
  2. public class HDemo1 {
  3.     private String name = "h demo 1";
  4.     public HDemo1() {
  5.         System.out.println(name);
  6.     }
  7. }
  8. @Component
  9. public class HDemo2 {
  10.     private String name = "h demo 2";
  11.     public HDemo2() {
  12.         System.out.println(name);
  13.     }
  14. }
复制代码
  1. @Component
  2. public class DemoBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements BeanFactoryAware {
  3.     private ConfigurableListableBeanFactory beanFactory;
  4.     @Override
  5.     public void setBeanFactory(BeanFactory beanFactory) {
  6.         if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
  7.             throw new IllegalArgumentException(
  8.                     "AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
  9.         }
  10.         this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
  11.     }
  12.     @Override
  13.     @Nullable
  14.     public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
  15.         // 在bean实例化之前做某些操作
  16.         if ("HDemo1".equals(beanName)) {
  17.             HDemo2 demo2 = beanFactory.getBean(HDemo2.class);
  18.         }
  19.         return null;
  20.     }
  21. }
复制代码
        将目标集中在postProcessBeforeInstantiation,这个方法在某个bean的实例化之前,会被调用,这就给了我们控制bean加载顺序的机会。
执行结果

场景2:希望某个bean在应用启动之后,首先实例化此Bean。
解决方法:重写DemoBeanPostProcessor的postProcessAfterInstantiation方法。
  1. @Component
  2. public class DemoBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
  3.                                    implements BeanFactoryAware {
  4.     private ConfigurableListableBeanFactory beanFactory;
  5.     @Override
  6.     public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
  7.         if ("application".equals(beanName)) {
  8.             beanFactory.getBean(FDemo.class);
  9.         }
  10.         return true;
  11.     }
  12. }
  13. @DependsOn("HDemo")
  14. @Component
  15. public class FDemo {
  16.     private String name = "F demo";
  17.     public FDemo() {
  18.         System.out.println(name);
  19.     }
  20. }
  21. @Component
  22. public class HDemo {
  23.     private String name = "H demo";
  24.     public HDemo() {
  25.         System.out.println(name);
  26.     }
  27. }
复制代码
执行结果(HDemo, FDemo的实例化顺序放在了最前面) 

其他网址

【基础系列】指定Bean初始化顺序的若干姿势 | 一灰灰Blog



来源:https://blog.csdn.net/feiying0canglang/article/details/125322580
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

北冰洋以北

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表