【问题标题】:Reinitialize spring @Autowire bean重新初始化spring @Autowire bean
【发布时间】:2018-10-04 22:22:08
【问题描述】:

我有一个场景,我需要在启动期间根据应用程序配置来初始化一个 bean。后来,由于基于事件获取的动态配置,我不得不更新 bean。

此 bean 无法更新,只能用新实例替换。

使用new 运算符是仅初始化本地实例还是会更改 bean?

@Component
public class TestComp {

  @Autowired
  private BeanA beanA;

  public void updateBean() {
    beanA = new BeanA("new value");
  }

}

我在另一个类中引用了 bean,并在使用 new 对其进行初始化后进行了检查。它反映了新对象。但是,如果确实如此,我需要专家的确认。

【问题讨论】:

  • 这会变得非常复杂。在最简单的情况下,假设您需要 Spring 对每个 BeanA 进行依赖注入,则在将 context 设置为 ApplicationContext 类型的自动关联实例变量之后,您需要使用它,而不是 new:beanA = context.getBean(BeanA.class)。而且您需要使 BeanA 成为原型范围的 bean - 因此每次调用 getBean() 方法时都会获得一个新的 BeanA。

标签: java spring autowired spring-bean


【解决方案1】:

我有一个场景,我需要在启动期间根据应用程序配置来初始化一个 bean。

没关系。单例作用域在这里是一个不错的选择。

稍后,由于基于事件获取的动态配置,我必须更新 bean。

这是个问题。在上下文中更新 bean 是一个复杂的过程:您需要删除现有的 bean 定义,添加一个新的定义,并更新与 bean 有某种关联的所有 bean(重新初始化这些组件,刷新上下文)。从技术上讲,这是可能的,并且已经通过 Spring Cloud 的@RefreshScope 进行了简化。

使用 new 操作符是只初始化本地实例还是会改变 bean?

它只影响这个类中的字段。没有人知道这一变化。 ApplicationContext#getBean 仍然会返回旧对象,并且所有组件都将(或已经)用旧实例初始化。

我在另一个类中引用了 bean,并在用 new 初始化它后检查了它。它反映了新对象。

这不可能。可能,它指的是TestComp#beanA 字段,而不是它自己的BeanA 字段。

根据您收到的事件,我建议的解决方案是to define a custom bean scope。它将保持 bean 和上下文更新。

【讨论】:

    【解决方案2】:

    听起来你想要一个工厂而不是。下面是大概的样子;您的需求可能会有所不同。

    @Component
    public class BeanFactory {
        private volatile BeanA beanAInstance;
    
        public BeanA createBeanA(String value) {
            if (null == beanAInstance) {
                synchronized (this) {
                    if (null == beanAInstance) {
                        beanAInstance = new BeanA(value);
                    }
                }
            }
            return beanAInstance;
        }
    
        public void refreshBeanA(String newValue) {
            synchronized (this) {
                beanAInstance = new BeanA(newValue);
            }
        }
    }
    

    然后将其连接起来,然后根据配置,您可以刷新并使用新值。请记住,这会改变您从该 bean 获得的值。

    【讨论】: