https://blog.csdn.net/boneix/article/details/73608573

学以致用之NamespaceHandlerSupport
2017年06月22日 16:58:36

阅读数:2265

前言:

看源码这事,也就一个兴趣而已。工作阅历随着时间增长,回望以前写的代码,粗糙而又简陋。刚入猿星人这行时,基本是对着被谩骂许久的《java从入门到精通》抄的。最近有看Spring 4.3.3的源码,正好做项目中有提到要优化MQ相关逻辑的意见。现如今就对着Spring的NamespaceHandlerSupport设计思路,抄它一抄。

项目需求背景:

1.MQ使用的是aliyun提供的消息队列,底层为RocketMQ,传输消息类型为byte

2.实现的代码结构如下,消费Message时,根据Message的Topic和Tag分别进行相关的处理,这里用的是if-else和switch

[java]  view plain copy
  1. public class AliyunMQListener implements MessageListener {
  2. private static final Logger logger = LoggerFactory.getLogger(AliyunMQListener.class);
  3. @Resource
  4. private MQConsumerService mqConsumerService;
  5. @Override
  6. public Action consume(Message message, ConsumeContext consumeContext) {
  7. try {
  8. String data = new String(message.getBody(), "UTF-8");
  9. logger.info("接收到消息,{}", data);
  10. if (message.getTopic().equals(SystemParamInit.getMQTopic())) {
  11. switch (message.getTag()) {
  12. case CommonValue.TOPIC_TAG:
  13. mqConsumerService.checkSensitiveWord4Topic(JsonUtils.toBean(data, TopicContent.class));
  14. break;
  15. case CommonValue.COMMENT_TAG:
  16. mqConsumerService.checkSensitiveWord4Comment(JsonUtils.toBean(data, CommentContent.class));
  17. break;
  18. case CommonValue.TOPIC_CREATOR_BANNED_TAG:
  19. mqConsumerService.updateUserTopic(JsonUtils.toBean(data, TopicCreatorBanned.class));
  20. break;
  21. }
  22. }
  23. } catch (Exception e) {
  24. logger.error("消息消费失败,{}", e);
  25. return Action.ReconsumeLater;
  26. }
  27. return Action.CommitMessage;
  28. }
  29. }

3.有同事提出,上面的实现方式不易维护,每多一个tag得加个case,每多一个topic得加一个if-else 这样做很蠢。

解决思路:

Spring的自定义标签解析是通过,写一个继承自NamespaceHandlerSupport的类,并实现init()方法,在init()方法中,去注册解析器。然后在解析xml时,通过约定的key去Map中拿到相应的解析器进行解析。大致思路有了,就开始对上面的逻辑进行改造。对应的设计模式为:接口-适配器模式、抽象工厂模式、策略模式及模板方法模式

首先,我们需要定义一个消息解析器接口,解析器的实现就是对相应tag的消息的处理

[java]  view plain copy
  1. public interface IMessageParser<T> {
  2. JmsAction parse(T message);
  3. }

然后,定义一个消息处理器接口,包含初始化方法、获取路径及接受Message实现消息分发的逻辑

[java]  view plain copy
  1. public interface IMessageHandler<T> {
  2. void init();
  3. String getDestination(T message);
  4. JmsAction parse(T message);
  5. }

接着,定义消息处理器的抽象类。形如NamespaceHandlerSupport。这边要实现消息的分发和解析器的注册

[java]  view plain copy
  1. public abstract class MessageHandlerSupport<T> implements IMessageHandler<T> {
  2. private final Map<String, IMessageParser> parsers = new ConcurrentHashMap<>();
  3. @Override
  4. public JmsAction parse(T message) {
  5. return findParserForMessage(message).parse(message);
  6. }
  7. private IMessageParser findParserForMessage(T message) {
  8. IMessageParser parser = this.parsers.get(getDestination(message));
  9. if (parser == null) {
  10. throw new MessageParserException("No MessageParser is matched,Destination is " + getDestination(message));
  11. }
  12. return parser;
  13. }
  14. protected final void registerMessageParser(String elementName, IMessageParser parser) {
  15. this.parsers.put(elementName, parser);
  16. }
  17. }

之后,定义一个处理器容器注册类。在Spring容器启动的时候,需要进行初始化,将需要的消息处理器放入其中,顺带提供个选择处理器的方法

[java]  view plain copy
  1. public class MessageHandlerRegister {
  2. private Map<String, IMessageHandler> container = new ConcurrentHashMap<>();
  3. public Map<String, IMessageHandler> getContainer() {
  4. return container;
  5. }
  6. public void setContainer(Map<String, IMessageHandler> container) {
  7. this.container = container;
  8. }
  9. public IMessageHandler findMessageHandler(String topicName) {
  10. if (CollectionUtils.isEmpty(container)) {
  11. return null;
  12. } else {
  13. return container.get(topicName);
  14. }
  15. }
  16. }

最后,再对原来的Listener进行调整

[java]  view plain copy
  1. public class AliyunMQListener implements MessageListener {
  2. private static final Logger logger = LoggerFactory.getLogger(AliyunMQListener.class);
  3. @Resource
  4. private MessageHandlerRegister messageHandlerRegister;
  5. @Override
  6. public Action consume(Message message, ConsumeContext context) {
  7. IMessageHandler messageHandler = messageHandlerRegister.findMessageHandler(message.getTopic());
  8. if (null == messageHandler) {
  9. logger.warn("No MessageHandler is matched,topic is {}", message.getTopic());
  10. } else {
  11. messageHandler.parse(message);
  12. }
  13. return Action.CommitMessage;
  14. }
  15. }

这时候,这代码就简洁多了。而且如果需要增加处理新的topic和tag,只要添加新的处理器和解析器然后进行注册。

后记:

aliyun的listener中有个ConsumeContext对象。

[java]  view plain copy
  1. package com.aliyun.openservices.ons.api;
  2. /**
  3. * 每次消费消息的上下文,供将来扩展使用
  4. */
  5. public class ConsumeContext {
  6. }

感觉么,consume(Message message, ConsumeContext context)这种格式和Spring里的parse(Element element, ParserContext parserContext)有点像。。

最后附上activemq和rabbitmq的测试代码。https://github.com/Boneix1992/Demos/tree/master/base-core/base-jms

学以致用之NamespaceHandlerSupport相关推荐

  1. 学以致提高学生操作计算机能力,【学以致用】提供学习平台,提升学生实际应用能力...

    原标题:[学以致用]提供学习平台,提升学生实际应用能力 我校教学服务部多年来一直承担学校电教设备的管理与维修任务,保障了全校教育教学及其他工作的顺利进行:同时,也成为学生学习实践的一个大平台. 近日, ...

  2. 记录方法用时_知识无穷,学以致用才是关键!四个方法教你学以致用

    写作社群里小伙伴在发日更文章,结果一群小伙伴点赞表扬,并且附上一句:"向你学习."可是要学习什么呢? 在我身边有许多爱学习的人,但并不是所有人都擅长学习,因为学习本身就是一种能力. ...

  3. 从 ReactiveCocoa 中能学到什么?不用此库也能学以致用

    从知道ReactiveCocoa开始就发现对这个库有不同的声音,上次参加<T>技术沙龙时唐巧对在项目中已全面使用FRP的代码家提出为什么这种编程模型出现了这么长时间怎么像ReactiveC ...

  4. 学以致用一 安装centos7.2虚拟机

    5说来惭愧,也是很久没来博客园了.距离上次写的已经快一年,只能说时间过的真的很快. 而如果这一年一直在坚持认真学习的话,收获肯定很多.然而我确又浪费了很多光阴,不得不恨这人生苦短. 在这一年里,小孩还 ...

  5. 学以致用 知行合一 ——《产品管理与研发项目管理》课程有感

    学以致用知行合一 在本次<产品管理与研发项目管理>的培训中,收益良多.按照曾老师的说法,沟通交流时客户现场有五类人,我个人定位就是"用户"类.所以要在事后迫不及待的跳出 ...

  6. 日拱一卒,集小胜为大胜; 学以致用,在战争中学习战争;

    项目,证明自己的学习能力: 短期掌握尽可能多的知识,并且连线总结成为网络 : 日拱一卒,集小胜为大胜: 学以致用,在战争中学习战争: 投入足够的时间: 学习中的总结,理解 ,应用,等编程套路: 目的性 ...

  7. 学习英文-学以致用【场景:程序员英文-开发环境】

    学习英文-学以致用. 场景说明 本期讨论下程序员的日常英文-开发环境相关. 场景对话 场景1: Student: Hi, How are you doing ? 最近怎么样? DaLiang: I a ...

  8. 学习英文-学以致用【场景:常说错的中式英文】

    学习英文-学以致用. 场景说明 今天介绍下常说错的中式英文? 场景对话 食物 DaLiang: This food is great, what ingredients are in this foo ...

  9. 【1小时记住Docker常用命令】Docker学以致用

    本文已被收录进专栏,谢谢支持.Docker一键部署,3小时学以致用

最新文章

  1. mysql设置约束l命令_2、MYSQL 基本数据库命令及约束
  2. R语言使用Repeat函数多次执行代码块内的语句,实现循环执行任务的功能:repeat没有提供任何检查条件,所以编码者必须给出退出重复循环的条件(一般使用if和break)
  3. Silverlight实例教程 - Out of Browser在线更新和Silent安装
  4. springmvc三十二:spring mvc的运行流程
  5. python 完全面向对象_史上最全的Python面向对象知识点疏理
  6. 使用poi写入doc文档中文档打不开_基于NodeJS和浏览器的PDF文档引擎——PDFKit
  7. Hive_ 对比分区,分桶
  8. Redis的主从搭建
  9. 2021年自然语言处理(NLP)算法学习路线!
  10. 17.看板方法——瓶颈和非即时可用资源笔记
  11. (Github)增强插件、脚本
  12. 安装kali之后那些事儿
  13. 台式计算机 行业标准,GBT 9813.3-2017 计算机通用规范 第3部分:服务器国家标准...
  14. ubuntu 14.04 install teamviewer
  15. MySQL闪退解决办法
  16. 巧用变量代换求极限 高数
  17. Mysql索引失效的几种情况总结
  18. Win7系统解决无法打开任务管理器
  19. 产业分析:视频云服务行业
  20. 文件流方式导出EXcle

热门文章

  1. 请大声说出我是猪 整蛊代码_大声笑的完整形式是什么?
  2. java解析遍历List集合(其实现子类)的三种方式
  3. window.open 详解
  4. 设置计算机上网环境,巧用win7家长控制设置保护孩子上网环境
  5. “七人分粥”- 介绍新书《软件过程管理》
  6. 【华为OD机试真题 Java】硬件产品销售方案
  7. MATLAB如何得到清晰的图
  8. ViewPager与ViewPager2的Adapter对比
  9. 一路向西——忆记2015
  10. php wdatepicker,WdatePicker日历控件使用方法