学以致用之NamespaceHandlerSupport
https://blog.csdn.net/boneix/article/details/73608573
学以致用之NamespaceHandlerSupport
前言:
看源码这事,也就一个兴趣而已。工作阅历随着时间增长,回望以前写的代码,粗糙而又简陋。刚入猿星人这行时,基本是对着被谩骂许久的《java从入门到精通》抄的。最近有看Spring 4.3.3的源码,正好做项目中有提到要优化MQ相关逻辑的意见。现如今就对着Spring的NamespaceHandlerSupport设计思路,抄它一抄。
项目需求背景:
1.MQ使用的是aliyun提供的消息队列,底层为RocketMQ,传输消息类型为byte
2.实现的代码结构如下,消费Message时,根据Message的Topic和Tag分别进行相关的处理,这里用的是if-else和switch
- public class AliyunMQListener implements MessageListener {
- private static final Logger logger = LoggerFactory.getLogger(AliyunMQListener.class);
- @Resource
- private MQConsumerService mqConsumerService;
- @Override
- public Action consume(Message message, ConsumeContext consumeContext) {
- try {
- String data = new String(message.getBody(), "UTF-8");
- logger.info("接收到消息,{}", data);
- if (message.getTopic().equals(SystemParamInit.getMQTopic())) {
- switch (message.getTag()) {
- case CommonValue.TOPIC_TAG:
- mqConsumerService.checkSensitiveWord4Topic(JsonUtils.toBean(data, TopicContent.class));
- break;
- case CommonValue.COMMENT_TAG:
- mqConsumerService.checkSensitiveWord4Comment(JsonUtils.toBean(data, CommentContent.class));
- break;
- case CommonValue.TOPIC_CREATOR_BANNED_TAG:
- mqConsumerService.updateUserTopic(JsonUtils.toBean(data, TopicCreatorBanned.class));
- break;
- }
- }
- } catch (Exception e) {
- logger.error("消息消费失败,{}", e);
- return Action.ReconsumeLater;
- }
- return Action.CommitMessage;
- }
- }
3.有同事提出,上面的实现方式不易维护,每多一个tag得加个case,每多一个topic得加一个if-else 这样做很蠢。
解决思路:
Spring的自定义标签解析是通过,写一个继承自NamespaceHandlerSupport的类,并实现init()方法,在init()方法中,去注册解析器。然后在解析xml时,通过约定的key去Map中拿到相应的解析器进行解析。大致思路有了,就开始对上面的逻辑进行改造。对应的设计模式为:接口-适配器模式、抽象工厂模式、策略模式及模板方法模式
首先,我们需要定义一个消息解析器接口,解析器的实现就是对相应tag的消息的处理
- public interface IMessageParser<T> {
- JmsAction parse(T message);
- }
然后,定义一个消息处理器接口,包含初始化方法、获取路径及接受Message实现消息分发的逻辑
- public interface IMessageHandler<T> {
- void init();
- String getDestination(T message);
- JmsAction parse(T message);
- }
接着,定义消息处理器的抽象类。形如NamespaceHandlerSupport。这边要实现消息的分发和解析器的注册
- public abstract class MessageHandlerSupport<T> implements IMessageHandler<T> {
- private final Map<String, IMessageParser> parsers = new ConcurrentHashMap<>();
- @Override
- public JmsAction parse(T message) {
- return findParserForMessage(message).parse(message);
- }
- private IMessageParser findParserForMessage(T message) {
- IMessageParser parser = this.parsers.get(getDestination(message));
- if (parser == null) {
- throw new MessageParserException("No MessageParser is matched,Destination is " + getDestination(message));
- }
- return parser;
- }
- protected final void registerMessageParser(String elementName, IMessageParser parser) {
- this.parsers.put(elementName, parser);
- }
- }
之后,定义一个处理器容器注册类。在Spring容器启动的时候,需要进行初始化,将需要的消息处理器放入其中,顺带提供个选择处理器的方法
- public class MessageHandlerRegister {
- private Map<String, IMessageHandler> container = new ConcurrentHashMap<>();
- public Map<String, IMessageHandler> getContainer() {
- return container;
- }
- public void setContainer(Map<String, IMessageHandler> container) {
- this.container = container;
- }
- public IMessageHandler findMessageHandler(String topicName) {
- if (CollectionUtils.isEmpty(container)) {
- return null;
- } else {
- return container.get(topicName);
- }
- }
- }
最后,再对原来的Listener进行调整
- public class AliyunMQListener implements MessageListener {
- private static final Logger logger = LoggerFactory.getLogger(AliyunMQListener.class);
- @Resource
- private MessageHandlerRegister messageHandlerRegister;
- @Override
- public Action consume(Message message, ConsumeContext context) {
- IMessageHandler messageHandler = messageHandlerRegister.findMessageHandler(message.getTopic());
- if (null == messageHandler) {
- logger.warn("No MessageHandler is matched,topic is {}", message.getTopic());
- } else {
- messageHandler.parse(message);
- }
- return Action.CommitMessage;
- }
- }
这时候,这代码就简洁多了。而且如果需要增加处理新的topic和tag,只要添加新的处理器和解析器然后进行注册。
后记:
aliyun的listener中有个ConsumeContext对象。
- package com.aliyun.openservices.ons.api;
- /**
- * 每次消费消息的上下文,供将来扩展使用
- */
- public class ConsumeContext {
- }
感觉么,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相关推荐
- 学以致提高学生操作计算机能力,【学以致用】提供学习平台,提升学生实际应用能力...
原标题:[学以致用]提供学习平台,提升学生实际应用能力 我校教学服务部多年来一直承担学校电教设备的管理与维修任务,保障了全校教育教学及其他工作的顺利进行:同时,也成为学生学习实践的一个大平台. 近日, ...
- 记录方法用时_知识无穷,学以致用才是关键!四个方法教你学以致用
写作社群里小伙伴在发日更文章,结果一群小伙伴点赞表扬,并且附上一句:"向你学习."可是要学习什么呢? 在我身边有许多爱学习的人,但并不是所有人都擅长学习,因为学习本身就是一种能力. ...
- 从 ReactiveCocoa 中能学到什么?不用此库也能学以致用
从知道ReactiveCocoa开始就发现对这个库有不同的声音,上次参加<T>技术沙龙时唐巧对在项目中已全面使用FRP的代码家提出为什么这种编程模型出现了这么长时间怎么像ReactiveC ...
- 学以致用一 安装centos7.2虚拟机
5说来惭愧,也是很久没来博客园了.距离上次写的已经快一年,只能说时间过的真的很快. 而如果这一年一直在坚持认真学习的话,收获肯定很多.然而我确又浪费了很多光阴,不得不恨这人生苦短. 在这一年里,小孩还 ...
- 学以致用 知行合一 ——《产品管理与研发项目管理》课程有感
学以致用知行合一 在本次<产品管理与研发项目管理>的培训中,收益良多.按照曾老师的说法,沟通交流时客户现场有五类人,我个人定位就是"用户"类.所以要在事后迫不及待的跳出 ...
- 日拱一卒,集小胜为大胜; 学以致用,在战争中学习战争;
项目,证明自己的学习能力: 短期掌握尽可能多的知识,并且连线总结成为网络 : 日拱一卒,集小胜为大胜: 学以致用,在战争中学习战争: 投入足够的时间: 学习中的总结,理解 ,应用,等编程套路: 目的性 ...
- 学习英文-学以致用【场景:程序员英文-开发环境】
学习英文-学以致用. 场景说明 本期讨论下程序员的日常英文-开发环境相关. 场景对话 场景1: Student: Hi, How are you doing ? 最近怎么样? DaLiang: I a ...
- 学习英文-学以致用【场景:常说错的中式英文】
学习英文-学以致用. 场景说明 今天介绍下常说错的中式英文? 场景对话 食物 DaLiang: This food is great, what ingredients are in this foo ...
- 【1小时记住Docker常用命令】Docker学以致用
本文已被收录进专栏,谢谢支持.Docker一键部署,3小时学以致用
最新文章
- mysql设置约束l命令_2、MYSQL 基本数据库命令及约束
- R语言使用Repeat函数多次执行代码块内的语句,实现循环执行任务的功能:repeat没有提供任何检查条件,所以编码者必须给出退出重复循环的条件(一般使用if和break)
- Silverlight实例教程 - Out of Browser在线更新和Silent安装
- springmvc三十二:spring mvc的运行流程
- python 完全面向对象_史上最全的Python面向对象知识点疏理
- 使用poi写入doc文档中文档打不开_基于NodeJS和浏览器的PDF文档引擎——PDFKit
- Hive_ 对比分区,分桶
- Redis的主从搭建
- 2021年自然语言处理(NLP)算法学习路线!
- 17.看板方法——瓶颈和非即时可用资源笔记
- (Github)增强插件、脚本
- 安装kali之后那些事儿
- 台式计算机 行业标准,GBT 9813.3-2017 计算机通用规范 第3部分:服务器国家标准...
- ubuntu 14.04 install teamviewer
- MySQL闪退解决办法
- 巧用变量代换求极限 高数
- Mysql索引失效的几种情况总结
- Win7系统解决无法打开任务管理器
- 产业分析:视频云服务行业
- 文件流方式导出EXcle