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;
- }
- }
解决思路:
Spring的自定义标签解析是通过,写一个继承自NamespaceHandlerSupport的类,并实现init()方法,在init()方法中,去注册解析器。然后在解析xml时,通过约定的key去Map中拿到相应的解析器进行解析。大致思路有了,就开始对上面的逻辑进行改造。对应的设计模式为:接口-适配器模式、抽象工厂模式、策略模式及模板方法模式
首先,我们需要定义一个消息解析器接口,解析器的实现就是对相应tag的消息的处理
- public interface IMessageParser<T> {
- JmsAction parse(T message);
- }
- public interface IMessageHandler<T> {
- void init();
- String getDestination(T message);
- JmsAction parse(T message);
- }
- 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);
- }
- }
- 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);
- }
- }
- }
- 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;
- }
- }
后记:
aliyun的listener中有个ConsumeContext对象。
- package com.aliyun.openservices.ons.api;
- /**
- * 每次消费消息的上下文,供将来扩展使用
- */
- public class ConsumeContext {
- }
最后附上activemq和rabbitmq的测试代码。https://github.com/Boneix1992/Demos/tree/master/base-core/base-jms