如何以编程方式刷新 spring bean

How to programmatically refresh spring bean

我们有一个 Spring 应用程序负责执行一些预计会启动的集成工作 运行 24 x 7。

我们使用的集成模式之一是使用一组集群 IBM MQ 队列管理器的 JMS 消息传递。对于集群化的 IBM MQ 队列管理器,如果其中一个队列管理器出现故障,您需要重新实例化队列连接工厂 bean,以便连接能够与仍在运行的其余队列管理器一起工作。

我们实施了 JMS 健康检查,并且 IBM MQ 队列管理器的状态发生了变化,我们以编程方式重新启动了 spring 上下文,因此重新创建了队列连接工厂 bean,并且一切正常。

然而,这对我们来说并不理想,因为我们应用程序中与 IBM MQ 无关的其他部分工作得很好,实际上可以在处理内容的过程中。因此,我们更愿意只重新创建 JMS 上下文,几乎是重新创建队列连接工厂 bean 和其他一些依赖于它们的 bean(消息侦听器容器、JmsTemplats 等),而不是重新启动整个应用程序上下文。

我找到了这个 post 并尝试实施解决方案 2)Delete & Register in registry 但我无法让它工作。

// If I call below:
beanFactory.destroySingleton(qcfName);
var qcf = jmsConfig.connectionFactory();
beanFactory.registerSingleton(qcfName, qcf);

// I get this exception:
Could not register object [org.springframework.cloud.sleuth.instrument.messaging.LazyConnectionFactory@5a0fb814] under bean name 'connectionFactory':
there is already object [org.springframework.cloud.sleuth.instrument.messaging.LazyConnectionFactory@5a0fb814] bound


// If I call below:
beanFactory.destroySingleton(qcfName);
beanFactory.removeBeanDefinition(qcfName);
var qcf = jmsConfig.connectionFactory();
beanFactory.registerSingleton(qcfName, qcf);

// I am getting this exception:
No bean named 'connectionFactory' available

我尝试了一些其他排列,例如 destroyBean,但我无法让它工作。

知道我做错了什么吗?我该如何解决? 预先感谢您的意见。

我明白你想做什么,所以我测试了几种方法并得到了这个:

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    @Autowired
    AnnotationConfigReactiveWebServerApplicationContext context;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        context.registerBean("jms", Bean1.class, () -> new Bean1());
        var jmsBean1 = context.getBean(Bean1.class);
        jmsBean1.hello();
        var jmsBean2 = context.getBean(Bean1.class);

        context.removeBeanDefinition("jms");

        context.registerBean("jms", Bean1.class, () -> new Bean1());
        var jmsBean3 = context.getBean(Bean1.class);

        System.out.println(jmsBean1.hashCode());
        System.out.println(jmsBean2.hashCode());
        System.out.println(jmsBean1 == jmsBean2);
        System.out.println(jmsBean3.hashCode());
        System.out.println(jmsBean1 == jmsBean3);

    }

}

class Bean1 {
    public void hello() {
        System.out.println("hello");
    }
}

结果是:

hello
405390165 // jmsBean1 hashcode -> singleton
405390165 //jmsBean2 hashcode -> singleton
true // jmsBean1 == jmsBean2
1182556970 // jmsBean3 hashcode
false // jmsBean3 is different object

如您所见,当您注册新 bean 时,默认的 bean 范围是单例。
注意 : 删除 bean 定义后尽量不要保留对旧对象的引用,因为 JVM GC

测试于: Spring 启动 (v2.6.3)
spring 框架 (5.3.15)

我的测试项目是 Reactive Web 项目,因为您看到 ReactiveApplicationContext 被注入,这取决于您必须注入上下文类型的项目。