首页 > 解决方案 > 如何使用 Spring Boot 监听动态目的地?

问题描述

我们有一个使用 Spring Boot 及其 JMS 工具的应用程序。在运行时,我们有不同的生产者联机并告诉我们的应用程序要收听的主题或队列的名称。现在,我们有:

@JmsListener(destination = "helloworld.q")
public void receive(String message) {
  LOGGER.info("received message='{}'", message);
}

当我们向主题发送消息时,它会起作用helloworld.q。问题是,直到运行时我们才知道主题的名称是什么,并且JmsListener似乎想要一个常量表达式。

消息生产者将挂钩到我们的 ActiveMQ 实例并广播一条消息,告诉我们需要开始收听他们的主题,例如“Wasabi”、“WhitePaper”、“SatelliteMajor”、“BigBoosters”等。没有办法知道运行时我们需要开始收听的主题。

我已经阅读了解释如何在运行时收听主题/队列的 Spring 文档(有点):

@Configuration
@EnableJms
public class ReceiverConfig implements JmsListenerConfigurer {

  @Override
  public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
    SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
    endpoint.setId("myJmsEndpoint");
    endpoint.setDestination("anotherQueue");
    endpoint.setMessageListener(message -> {
        // processing
    });
    registrar.registerEndpoint(endpoint);
  }

  // other methods...
}

我已经将它作为测试推送到我们的 Receiver 配置中,当我们发送消息时它确实会被调用。问题是,Spring 让所有这些东西都被自动调用,我们不知道在哪里以及如何给这个方法提供端点需要监听的主题/队列的名称。此外,消息侦听器似乎永远不会被调用,但这是一个单独的问题;如果我们至少可以发送自定义主题或队列以供其收听,我相信我们可以解决它。

我们正在使用 Spring 2.x

标签: javaspring-bootspring-jms

解决方案


您可以使用属性占位符作为目标名称

@SpringBootApplication
public class So56226984Application {

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

    @JmsListener(destination = "${foo.bar}")
    public void listen(String in) {
        System.out.println(in);
    }

    @Bean
    public ApplicationRunner runner(JmsTemplate template) {
        return args -> template.convertAndSend("baz", "qux");
    }

}

然后设置属性,例如在 application.yml 中为 Spring Boot 应用程序设置属性,或者在启动 JVM 时设置命令行属性

-Dfoo.bar=baz

编辑

您可以使侦听器 bean 成为原型并调整环境属性。

@SpringBootApplication
public class So56226984Application {

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

    @Bean
    public ApplicationRunner runner(JmsTemplate template, JmsListenerEndpointRegistry registry,
            ConfigurableApplicationContext context) {

        return args -> {
            Scanner scanner = new Scanner(System.in);
            String queue = scanner.nextLine();
            Properties props = new Properties();
            context.getEnvironment().getPropertySources().addLast(new PropertiesPropertySource("queues", props));
            while (!"quit".equals(queue)) {
                System.out.println("Adding " + queue);
                props.put("queue.name", queue);
                context.getBean("listener", Listener.class);
                template.convertAndSend(queue, "qux sent to " +  queue);
                System.out.println("There are now " + registry.getListenerContainers().size() + " containers");
                queue = scanner.nextLine();
            }
            scanner.close();
        };
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Listener listener() {
        return new Listener();
    }

    public static class Listener {

        @JmsListener(destination = "${queue.name}")
        public void listen(String in) {
            System.out.println(in);
        }

    }
}

推荐阅读