springboot+redis自定义注解实现发布订阅的实现代码

 更新时间:2023年08月14日 08:54:52   作者:在下uptown  
在Redis中客户端可以通过订阅特定的频道来接收发送至该频道的消息,本文主要介绍了springboot+redis自定义注解实现发布订阅,具有一定的参考价值,感兴趣的可以了解一下

前言

最近开发了一个内部消息组件,逻辑大体是通过定义注解 @MessageHub,在启动时扫描全部bean中有使用了该注解的方法后台创建一个常驻线程代理消费数据,当线程消费到数据就回写到对应加了注解的方法里。

@Slf4j
@Service
public class RedisConsumerDemo {
    @MessageHub(topic = "${uptown.topic}", type = "REDIS_PUBSUB")
    public void consumer(Object message) {
        log.info("pubsub info {} ", message);
    }   
}

实现redis的队列、stream方式实现都很简单,唯独发布订阅方式,网上的demo全都是一个固定套路,通过redis容器注入监听器,而且回写非常死板。那么如何将这块的逻辑统一呢。之前总结过消息组件的代码设计,这里贴一下链接。

内部消息通道组件

常规写法

常规实现reids的发布订阅模式写法一共三步

创建消息监听器

@Bean 
public MessageListenerAdapter smsExpirationListener(TestSubscriber messageListener) {
    return new MessageListenerAdapter(messageListener, "onMessage");
}

创建订阅器

@Component
public class TestSubscriber implements MessageListener {
    @Override
    public void onMessage(Message message, byte[] pattern) {
        log.info("get data :{}", msg);
    }
}

向redis容器中添加消息监听器

@Configuration
public class RedisConfig {
    @Bean
    public RedisMessageListenerContainer container(
        RedisConnectionFactory redisConnectionFactory,
        MessageListenerAdapter smsExpirationListener) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        container.addMessageListener(smsExpirationListener, new PatternTopic("test"));
        return container;
    }
}

这样定义非常简单明了,但是有个问题是太代码僵硬了,创建监听者很不灵活,只能指定内部的onMessage方法,那么怎么才能融入到我们的内部消息流转中间件里呢。

自定义注解实现

我们内部组件抽象了两个方法,生产和消费,但这两个方法逻辑截然不同,生产方法是暴露给serverice层接口调用,调用方在调用生产方法后能直接知道生产了几条数据和成功与否。而消费方法是配合Spring生命周期函数服务启动时建立常驻消费线程的。

/**
 * 生产消息
 */
Integer producer(MessageForm messageForm);
/**
 * 消费消息
 */
void consumer(ConsumerAdapterForm adapterForm);

生产消息当然很容易实现,只需要调用已经封装好的convertAndSend方法。

stringRedisTemplate.convertAndSend(messageForm.getTopic(), messageForm.getMessage());

消费方法就有说法了,动态生成监听者的场景下使用redis容器用代码挨个注册已经满足不了了,但仔细过一遍源代码就会发现,监听类的构造方法的入参只有两个,第一个需要回调的代理类,第二个消费到数据后回调的方法。

/**
 * Create a new {@link MessageListenerAdapter} for the given delegate.
 *
 * @param delegate the delegate object
 * @param defaultListenerMethod method to call when a message comes
 * @see #getListenerMethodName
 */
public MessageListenerAdapter(Object delegate, String defaultListenerMethod) {
   this(delegate);
   setDefaultListenerMethod(defaultListenerMethod);
}

那么好了好了,方案有了,本质上就是把RedisMessageListenerContainer注入进来之后,扫描项目里所有加了 @MessageHub 的bean,包装成监听类加载到容器里就完事了。怎么扫描的代码就不再赘述了,实现Spring的生命周期函数BeanPostProcessor#postProcessAfterInitialization,在这里用AnnotationUtils判断是否标注了注解。

MessageHub annotation = AnnotationUtils.findAnnotation(method, MessageHub.class);
if (annotation == null) {
    continue;
}

标注了后判断如果是发布订阅,进入发布订阅的实现类。

@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Service("redisPubSubProcessor")
public class RedisPubSubProcessor extends MessageHubServiceImpl {
    @Resource
    RedisMessageListenerContainer redisPubSubContainer;
    @Override
    public void produce(ProducerAdapterForm producerAdapterForm) {
        stringRedisTemplate.convertAndSend(producerAdapterForm.getTopic(), producerAdapterForm.getMessage());
    }
    @Override
    public void consume(ConsumerAdapterForm messageForm) {
        MessageListenerAdapter adapter = new MessageListenerAdapter(messageForm.getBean(), messageForm.getInvokeMethod().getName());
        adapter.afterPropertiesSet();
        redisPubSubContainer.addMessageListener(adapter, new PatternTopic(messageForm.getTopic()));
    }
    @Bean
    public RedisMessageListenerContainer redisPubSubContainer(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}

首先先将RedisMessageListenerContainer注入到Spring容器里,produce方法只需要调用下现程的api。consume方法由于上一步我们获取了bean和对应的method,直接用MessageListenerAdapter的构造器创建出监听器来,这里有个坑,需要手动调用adapter.afterPropertiesSet()设置一些必要的属性,这个在常规写法里框架帮忙做了。如果不调用的话会出一些空指针之类的bug。

随后把监听器add到容器就实现了方法代理,背后的线程监听到数据会回调到标注了 @MessageHub 的方法里

到此这篇关于springboot+redis自定义注解实现发布订阅的实现代码的文章就介绍到这了,更多相关springboot redis发布订阅内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中String、StringBuffer和StringBuilder的区别

    Java中String、StringBuffer和StringBuilder的区别

    这篇文章主要介绍了Java中String、StringBuffer和StringBuilder的区别,StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串char[]value但是没有final关键字修饰,所以这两个可变,需要的朋友可以参考下
    2024-01-01
  • Java实现根据前端所要格式返回树形3级层级数据

    Java实现根据前端所要格式返回树形3级层级数据

    这篇文章主要为大家详细介绍了Java如何实现根据前端所要格式返回树形3级层级数据,文中的示例代码讲解详细,有需要的小伙伴可以了解下
    2024-02-02
  • Java List集合排序实现方法解析

    Java List集合排序实现方法解析

    这篇文章主要介绍了Java List集合排序实现方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Java实现权重随机算法详解

    Java实现权重随机算法详解

    平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的。本文就详细来介绍如何实现,感兴趣的可以了解一下
    2021-07-07
  • Java方法上注解值修改不成功的问题

    Java方法上注解值修改不成功的问题

    这篇文章主要介绍了Java方法上注解值修改不成功的解决方法,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • IDEA如何导入已有Maven项目

    IDEA如何导入已有Maven项目

    导入Maven项目到IDEA时,经常遇到问题,本文记录正确步骤,首先创建空项目,然后选择Import Module from external model并选择Maven,勾选所需profiles和Maven项目,最后设置Project SDK,这样配置后,项目结构将符合预期,仅显示一个Module
    2024-11-11
  • java实现双色球机选号码生成器

    java实现双色球机选号码生成器

    这篇文章主要为大家详细介绍了java实现双色球机选号码生成器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-01-01
  • Spring实战之Bean定义中的SpEL表达式语言支持操作示例

    Spring实战之Bean定义中的SpEL表达式语言支持操作示例

    这篇文章主要介绍了Spring实战之Bean定义中的SpEL表达式语言支持操作,结合实例形式分析了Bean定义中的SpEL表达式语言操作步骤与实现技巧,需要的朋友可以参考下
    2019-12-12
  • java Hibernate save()与persist()区别

    java Hibernate save()与persist()区别

    本文章来给各位同学介绍一下Hibernate save()与persist()区别,希望此文章能对各位同学对于Hibernate save()与persist()有所理解
    2016-01-01
  • java通过DelayQueue实现延时任务

    java通过DelayQueue实现延时任务

    本文主要介绍了java通过DelayQueue实现延时任务,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07

最新评论