SpringBoot @JmsListener(destination = ) 运行时动态修改

背景

最近在写一个Mqtt消息转发的中间件,可通过ActvieMq接收消息。需要对外提供配置接口,通过配置接口,动态配置Queue,可接收Queue的消息。

整个项目依赖于SpringBoot ,通过SpringBoot实现队列消费,只需要通过@JmsListener(destination = "queueName") 注解,就可以实现对特定队列的消费。

遇到问题

正式这种注解的方式,使得destination只能在代码中写死,没法动态修改。 而我想实现的效果是能够动态的修改destination,动态的创建Consumer。

解决方案

  1. 继续使用@JmsListener,找到某种方式,能够动态修改destination
  2. 不使用@JmsListener,通过ActiveMq提供的Jar,手动实现连接、消费等。之前通过这种方式实现过RabbitMq的处理,因此该方案不会有难度,只是工作量多一些。

由于SpringBoot的过分简单,因此开始尝试通过第一种方式。

解决过程

一开始想着通过反射修改注解destination但是尝试失败。后来想到@JmsListener(destination = "${xxx}")这种方式,根据配置文件,可以修改消费的队列,但是需要从新启动。 因此能不能动态修改配置文件对应的变量,然后消费者动态注入Spring。

  1. 自定义实现一个MapPropertySource,然后加入到Environment中

@Configuration
public class DynamicTestSetting {

public static final String DYNAMIC_CONFIG = "dynamic_settting";
@Autowired
AbstractEnvironment environment;

@PostConstruct
public void init() {
    environment.getPropertySources().addFirst(new DynamicLoadPropertySource(DYNAMIC_CONFIG, null));
}

}

@Slf4j
public class DynamicLoadPropertySource extends MapPropertySource {

private static Map<String, Object> map = new ConcurrentHashMap<String, Object>(64);

private static ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1);
static {
    scheduled.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            //测试:定时修改value
            //真正使用可能需要传递一个参数或者定时读取配置文件
            map.put("test", String.valueOf(System.currentTimeMillis()));
        }
    }, 1, 1, TimeUnit.SECONDS);
}

public DynamicLoadPropertySource(String name, Map<String, Object> source) {
    super(name, map);
}


@Override
public Object getProperty(String name) {
    return map.get(name);
}

}

2.消费者需要动态加入
@Slf4j
public class TestConsumer {

@JmsListener(destination = "${test}")
public void receiveQueue(BytesMessage msg) {
    //接收消息后的处理逻辑
}

}

/*动态注入bean到spring,需要用到ApplicationContext/
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();

        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestConsumer.class);
        defaultListableBeanFactory.registerBeanDefinition("testConsumer",definitionBuilder.getBeanDefinition());

//调用getBeaan时Spring会创建一个TestConsumer实例,这个时候ActiveMq中会创建一个destination = "${test}"队列,TestConsummer和其绑定消费

        TestConsumer consumer = context.getBean("testConsumer", TestConsumer.class);
        System.out.println(consumer);
        
        

动态关闭Queue的消费者

参见另一篇:https://segmentfault.com/n/13...

参考文章:https://blog.csdn.net/qq_3491...

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值