# 【公众号开发】公众号技术分享第三期 - 公众号事件处理 - 设计模式实战分享
# 由于内容过于干燥。brath 趁机打一波广告…
由 Brath 全栈开发的程序员刷题应用 - 面试记 现已经全面开放测试。
在面试记,你可以:获取与分享编程知识与面经,数十万道题目供你选择,通过喜爱标签自由组卷,模拟面试,最新支持 GPT3.5 免费使用…
由于时间原因,更多功能还在慢慢开发中,如果你对面试记感兴趣,想要共同开发的话,请联系我的微信:【Brath_code】
# 面试记 APP
Github:https://github.com/Guoqing815/interview
安卓 APP 下载:https://www.pgyer.com/interview_app_release
Brath 的个人博客:https://brath.top
面试记官方公众号,定期分享有趣的编程知识:https://mp.weixin.qq.com/s/jWs6lLHl5L-atXJhHc4YvA
# 前言
上期我们介绍了用非常优雅的方式来实现发送模板消息,本期将结合工厂模式 + 策略模式来同样优雅的实现公众号事件处理。
# 首先来回顾下工厂和策略模式💻
# 工厂模式:
工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式,而不必暴露对象创建的逻辑。在工厂模式中,我们定义一个工厂接口,该接口有一个或多个方法用于创建对象。然后,我们实现这个接口来创建具体的对象。这样,我们就可以在不暴露对象创建逻辑的情况下创建对象。
# 策略模式:
策略模式是一种行为型设计模式,它允许在运行时动态地改变对象的行为。在策略模式中,我们定义一系列算法,将每个算法封装到一个独立的类中,并让它们可以相互替换。这样可以使得算法的变化独立于使用它的客户端。
# 正文开始:
# 一、设计思想
# 1. 事件策略:将许多类型的事件分为不同的策略,例如文本消息、图片消息、事件消息等等。
# 2. 事件工厂:储存所有事件信息,对外提供获取事件处理器的方法。
# 3. 事件解析:存储事件工厂,对外提供解析方法,返回解析后的实体对象 WechatMessage
# 4. 事件处理:通过工厂提供的获取事件处理器的方法,配合传来的事件类型获取具体处理器在进行消息处理。
# 二、结构预览
# eventProcessing 包,定义事件处理 POJO 对象、处理器、处理器工厂、解析器。
# handlers 包,定义多种事件消息策略处理器。
预览了结构之后,可以更好的理解下面的代码实现
# 三、策略消息以及实现类型
现在开始实现代码的部分,首先我们来定义事件对象、事件消息接口、基础实现
# 事件对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
@Data @Accessors(chain = true) public class WechatMessage { private String toUserName; private String fromUserName; private long createTime; private String msgType; private String content; private String msgId; private String event; private String eventKey; private String ticket; private String latitude; private String longitude; private String precision; private String picUrl; private String mediaId; private String format; private String thumbMediaId; private String location_X; private String location_Y; private String scale; private String label; private String title; private String description; private String url; private String musicUrl; private String hqMusicUrl; private String recognition; private String encrypt; }
|
# 事件消息接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
public interface WechatMessageHandler {
boolean supports(String messageType);
void parseMessage(WechatMessage wechatMessage, Element xmlElement);
String handleMessage(WechatMessage wechatMessage, HttpServletRequest request);
String getHandleName(); }
|
# 实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Data @Component(EventConsts.TEXT_MESSAGE_HANDLER) @ApiModel(value = "文本消息处理器") @Accessors(chain = true) public class TextMessageHandler implements WechatMessageHandler {
private Logger logger = LoggerFactory.getLogger(TextMessageHandler.class);
@Resource private TextReplyService textReplyService;
@Override public String getHandleName() { return EventConsts.TEXT_MESSAGE_HANDLER; }
@Override public boolean supports(String messageType) { return "text".equals(messageType); }
@Override public void parseMessage(WechatMessage wechatMessage, Element xmlElement) { String content = xmlElement.elementText("Content"); wechatMessage.setContent(content); }
@Override public String handleMessage(WechatMessage wechatMessage, HttpServletRequest request) { logger.info("收到文本消息 wechatMessage: {}", wechatMessage); } }
|
注意:EventConsts 为常量,可以自己定义:
1
| public static final String TEXT_MESSAGE_HANDLER = "textMessageHandler";
|
# 这两段代码实现了一个事件消息处理规范接口,以及具体的处理器实现
# 四、事件工厂,事件处理器实现
# 事件工厂:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
@Component public class WechatMessageHandlerFactory {
private List<WechatMessageHandler> messageHandlers;
@Resource private void initService(WechatMessageHandler[] properties) { messageHandlers = Arrays.asList(properties); }
public WechatMessageHandler getMessageHandler(String messageType) { return messageHandlers.stream() .filter(handler -> handler.supports(messageType)) .findFirst() .orElse(null); } }
|
# 事件处理器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @Component public class WechatMessageParser {
private static WechatMessageHandlerFactory wechatMessageHandlerFactory;
@Resource private void setWechatMessageHandlerFactory(WechatMessageHandlerFactory wechatMessageHandlerFactory) { WechatMessageParser.wechatMessageHandlerFactory = wechatMessageHandlerFactory; }
public static WechatMessage parse(String xml) { try { Document document = DocumentHelper.parseText(xml); Element rootElement = document.getRootElement();
String messageType = rootElement.elementText("MsgType");
WechatMessage wechatMessage = new WechatMessage() .setToUserName(rootElement.elementText("ToUserName")) .setFromUserName(rootElement.elementText("FromUserName")) .setCreateTime(Long.parseLong(rootElement.elementText("CreateTime"))) .setMsgType(messageType);
Optional<WechatMessageHandler> optionalMessageHandler = Optional.ofNullable(wechatMessageHandlerFactory.getMessageHandler(messageType)); optionalMessageHandler.ifPresent(handler -> handler.parseMessage(wechatMessage, rootElement));
return wechatMessage; } catch (DocumentException e) { throw new RuntimeException(e); } } }
|
# 这两段代码我们实现了事件工厂以及事件解析器的实现。
# 到此为止,整条链路打通,可以来单元测试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class SpringRunnerTest {
private Logger logger = LoggerFactory.getLogger(SpringRunnerTest.class);
@Resource private WechatMessageHandlerFactory wechatMessageHandlerFactory;
@Test public void testHandler() { WechatMessageHandler textHandler = wechatMessageHandlerFactory.getMessageHandler("text"); System.out.println(textHandler.getHandleName());
WechatMessageHandler location = wechatMessageHandlerFactory.getMessageHandler("location"); System.out.println(location.getHandleName());
WechatMessage wechatMessage = WechatMessageParser.parse("<xml><ToUserName><! [CDATA[gh_6ecd244c13d6]]></ToUserName>\n" + "<FromUserName><![CDATA[ow3gF5zkvJc097jaYvLj5uWKZZTk]]></FromUserName>\n" + "<CreateTime>1686546944</CreateTime>\n" + "<MsgType><![CDATA[text]]></MsgType>\n" + "<Content><![CDATA[测试]]></Content>\n" + "<MsgId>24145550072327732</MsgId>\n" + "</xml>\n"); System.out.println(wechatMessage);
MockHttpServletRequest request = new MockHttpServletRequest(); String content = textHandler.handleMessage(wechatMessage, request); System.out.println("content: " + content); } }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| textMessageHandler
locationMessageHandler
WechatMessage(toUserName=gh_6ecd244c13d6, fromUserName=ow3gF5zkvJc097jaYvLj5uWKZZTk, createTime=1686546944, msgType=text, content=测试, msgId=null, event=null, eventKey=null, ticket=null, latitude=null, longitude=null, precision=null, picUrl=null, mediaId=null, format=null, thumbMediaId=null, location_X=null, location_Y=null, scale=null, label=null, title=null, description=null, url=null, musicUrl=null, hqMusicUrl=null, recognition=null, encrypt=null)
content: <xml> <ToUserName><![CDATA[ow3gF5zkvJc097jaYvLj5uWKZZTk]]></ToUserName> <FromUserName><![CDATA[gh_6ecd244c13d6]]></FromUserName> <CreateTime><![CDATA[1686550406094]]></CreateTime> <MsgType><![CDATA[text]]></MsgType> <FuncFlag><![CDATA[0]]></FuncFlag> <Content><![CDATA[测试成功]]></Content> </xml>
|
# 测试成功~
# tips:如果你觉得 Brath 分享的代码还可以的话,请将我分享给更多需要帮助的人~
# 到此为止,公众号事件处理 - 设计模式的知识分享就结束啦,还请同学们多多关注 InterviewCoder,做一个激进的开发者,为了更好的你,也为了更好的世界!
# 完结撒花❀
# 关于我
Brath 是一个热爱技术的 Java 程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!
非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!