在成功接入微信公众平台之后(如何接入请参考《微信公众号开发:账号申请与接入》),就可以对微信服务器POST过来的消息或者事件XML数据包进行监听与处理了。

在《微信公众号开发:账号申请与接入》的 WeChatController 控制器中, handleMsgAndEvent() 方法用来监听并处理消息与事件,示例项目的完整目录层次如下图所示。

本示例使用了Maven来构建工程,除了要引入基本的SpringMVC-WEB依赖,还需引入以下三个工具包。

     <dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId></dependency><dependency><groupId>xstream</groupId><artifactId>xstream</artifactId><version>1.2.2</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>

WeChatController 控制器和 WeChatEncrypt  加密工具类的完整代码在《微信公众号开发:账号申请与接入》中已经贴出,此章中不再重复贴出。
WechatSession 是处理所有消息的入口。

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerFactory;import org.w3c.dom.Document;
import org.xml.sax.SAXException;/*** 处理消息和事件的入口类*/
public class WechatSession{private InputStream in;private PrintWriter out;public static TransformerFactory tffactory = TransformerFactory.newInstance();private static DocumentBuilder documentBuilder = null;public static DocumentBuilder getDocumentBuilder(){// 先检查实例是否已创建,如果未创建才进入同步块if (null == documentBuilder){synchronized (WechatSession.class){// 再次检查实例是否已创建,如果真的未创建才创建实例if (null == documentBuilder){DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();try{documentBuilder = factory.newDocumentBuilder();}catch (ParserConfigurationException e){e.printStackTrace();}}}}return documentBuilder;}/*** 构造方法* * @param in* @param out*/public WechatSession(InputStream in, PrintWriter out){this.in = in;this.out = out;}/*** 对接收到的消息和事件进行处理*/public void process(){try{Document document = getDocumentBuilder().parse(in);String msgType = document.getElementsByTagName("MsgType").item(0).getTextContent();if ("text".equals(msgType)){TextMsg msg = new TextMsg();msg.read(document);msg.onTextMsg(out);}else if ("event".equals(msgType)){EventMsg msg = new EventMsg();msg.read(document);msg.onEventMsg(out);}}catch (SAXException e){e.printStackTrace();}catch (IOException e){e.printStackTrace();}}public void close(){try{if (in != null){in.close();}if (out != null){out.flush();out.close();}}catch (IOException e){e.printStackTrace();}}}

AbstractMsg 是普通消息与事件消息公用的抽象父类,包含了一些通用的基础属性、提供了一些通用的基础方法。

import java.io.IOException;
import java.io.StringWriter;import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;import lombok.Data;/*** 消息与事件的抽象父类*/
@Data
public abstract class AbstractMsg
{protected String toUserName;protected String fromUserName;protected String createTime;protected String msgType;/*** 读取Document内容到Java对象* * @param document*/public void read(Document document){readHead(document);readBody(document);}/*** 获取Java对象的XML字符串*/public String write(){Document document = WechatSession.getDocumentBuilder().newDocument();Element root = document.createElement("xml");writeHead(root, document);writeBody(root, document);document.appendChild(root);return readDocument(document);}/*** 读取消息和事件公有的一些基础属性* * @param document*/private void readHead(Document document){toUserName = document.getElementsByTagName("ToUserName").item(0).getTextContent();fromUserName = document.getElementsByTagName("FromUserName").item(0).getTextContent();createTime = document.getElementsByTagName("CreateTime").item(0).getTextContent();msgType = document.getElementsByTagName("MsgType").item(0).getTextContent();}/*** 消息和事件具体子类需实现该方法,用来读取一些自身独有的属性数据* * @param document*/protected abstract void readBody(Document document);/*** 将Java对象中的基础属性写入Document* * @param root* @param document*/private void writeHead(Element root, Document document){Element toUserNameElement = document.createElement("ToUserName");CDATASection toUserNameCData = document.createCDATASection(this.toUserName);toUserNameElement.appendChild(toUserNameCData);Element fromUserNameElement = document.createElement("FromUserName");CDATASection fromUserNameCData = document.createCDATASection(this.fromUserName);fromUserNameElement.appendChild(fromUserNameCData);Element createTimeElement = document.createElement("CreateTime");createTimeElement.setTextContent(this.createTime);Element msgTypeElement = document.createElement("MsgType");CDATASection msgTypeCData = document.createCDATASection(this.msgType);msgTypeElement.appendChild(msgTypeCData);root.appendChild(toUserNameElement);root.appendChild(fromUserNameElement);root.appendChild(createTimeElement);root.appendChild(msgTypeElement);}/*** 消息和事件各子类需实现该方法,用来写入一些自身独有的属性数据* * @param root* @param document*/protected abstract void writeBody(Element root, Document document);/*** 获取Document对象中指定元素的内容* * @param document* @param elementName* @return*/protected String getElementContent(Document document, String elementName){if (document.getElementsByTagName(elementName).getLength() > 0){return document.getElementsByTagName(elementName).item(0).getTextContent();}return null;}/*** 读取Document对象为XML字符串* * @param document*/private String readDocument(Document document){String docXml = "";StringWriter writer = new StringWriter();try{Transformer transformer = WechatSession.tffactory.newTransformer();transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");// 设置编码字符集transformer.setOutputProperty(OutputKeys.INDENT, "yes");// 设置缩进transformer.transform(new DOMSource(document), new StreamResult(writer));docXml = writer.getBuffer().toString();System.out.println(docXml);// 将获取到的XML字符串打印至控制台writer.close();}catch (Exception e){e.printStackTrace();}finally{if (null != writer){try{writer.close();}catch (IOException e){e.printStackTrace();}}}return docXml;}}

TextMsg 普通消息Java类。

import java.io.PrintWriter;import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;import lombok.Data;
import lombok.EqualsAndHashCode;/*** 普通文本消息*/
@Data
@EqualsAndHashCode(callSuper = false)
public class TextMsg extends AbstractMsg
{private String content;private String msgId;/*** 从Document中读取普通文本消息独有的属性数据*/@Overrideprotected void readBody(Document document){content = document.getElementsByTagName("Content").item(0).getTextContent();msgId = document.getElementsByTagName("MsgId").item(0).getTextContent();}/*** 回复时专用,将普通文本消息独有的回复内容写入Document中*/@Overrideprotected void writeBody(Element root, Document document){Element contentElement = document.createElement("Content");CDATASection contentCData = document.createCDATASection(this.content);contentElement.appendChild(contentCData);root.appendChild(contentElement);}public void onTextMsg(PrintWriter out){System.out.println("此处添加处理普通文本消息的业务逻辑,此处简单回复:你好 某某某");TextMsg msg = new TextMsg();msg.setFromUserName(this.toUserName);msg.setToUserName(this.fromUserName);msg.setCreateTime(this.createTime);msg.setMsgType("text");msg.setContent("你好 " + this.fromUserName);out.print(msg.write());}}

EventMsg 事件消息Java类。

import java.io.PrintWriter;import lombok.Data;
import lombok.EqualsAndHashCode;import org.w3c.dom.Document;
import org.w3c.dom.Element;/*** 事件统一处理类*/
@Data
@EqualsAndHashCode(callSuper = false)
public class EventMsg extends AbstractMsg {// 事件列表:CLICK(点击自定义菜单)/subscribe(关注)/unsubscribe(取消关注)/SCAN(扫描二维码)/LOCATION(上报地理位置)private String event;private String eventKey;private String ticket;private String latitude;private String longitude;private String precision;/*** 从Document中读取各事件独有的属性数据*/@Overrideprotected void readBody(Document document) {event = getElementContent(document, "Event").toLowerCase();switch (event) {case "click": {eventKey = getElementContent(document, "EventKey");break;}case "subscribe":case "unsubscribe":case "scan": {eventKey = getElementContent(document, "EventKey");ticket = getElementContent(document, "Ticket");break;}case "location": {latitude = getElementContent(document, "Latitude");longitude = getElementContent(document, "Longitude");precision = getElementContent(document, "Precision");break;}default: {break;}}}/*** 回复时专用,由于事件无须回复,在此空实现*/@Overrideprotected void writeBody(Element root, Document document) {}public void onEventMsg(PrintWriter out) {System.out.println("此处添加处理事件的业务逻辑,对于关注事件回复:尊敬的 某某某 女士/先生,欢迎您关注我的个人公众号!");switch (event) {case "subscribe": {TextMsg msg = new TextMsg();msg.setFromUserName(this.toUserName);msg.setToUserName(this.fromUserName);msg.setCreateTime(this.createTime);msg.setMsgType("text");msg.setContent("尊敬的 " + this.fromUserName + " 女士/先生,欢迎您关注我的个人公众号!");out.print(msg.write());break;}}}
}

本文项目源码已上传至CSDN,资源地址:http://download.csdn.net/download/pengjunlee/10257857

微信公众号开发:消息与事件处理相关推荐

  1. 该微信用户未开启“公众号安全助手”的消息接收功能,请先开启后再绑定,Java微信公众号开发消息推送公众号用户绑定问题 的解决办法

    问题概述 在进行微信公众号开发的时候遇到的这个问题,通过Web开发公众号的模板消息推送,在调试的过程中,需要进行开发者接口联调&调试,在调试之前需要将当前的公众号与用户的微信号进行绑定, 绑定 ...

  2. 微信公众号开发--消息接收与回复

    在上篇文章中已经说明如何接入微信服务器,此篇直接接着上篇文章介绍公众号的消息接收与回复,可以结合上篇文章阅读本文,微信公众号开发–服务器接入. 微信公众号开发接口文档: https://mp.weix ...

  3. 微信公众号开发消息推送以及图文推送

    今天给大家分享的关注公众号自动推送图文消息,以及做一个超牛逼的机器人. 先看看效果. 发错图了...这是我昨天开发的一款机器人chu了会骂人啥都不会了.我今天将它词库进行了更新和升级,接入了http: ...

  4. 微信公众号开发的一些方法总结

    概述 微信公众号开发,其实就是微信使用者.微信公众号平台和自身服务器的http消息交互:在这一系列过程中,微信公众号平台充当了中介和转发作用(如图1所示).需要注意的是,微信公众号平台向自身服务器转发 ...

  5. 微信公众号开发 - 事件处理和回复消息

    微信公众号开发文章目录 1.微信公众号开发 - 环境搭建 2.微信公众号开发 - 配置表设计以及接入公众号接口开发 3.微信公众号开发 - token获取(保证同一时间段内只请求一次) 4.微信公众号 ...

  6. 微信公众号开发C#系列-7、消息管理-接收事件推送

    1.概述 在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息.其中,某些事件推送在发生后,是允许 ...

  7. C#微信公众号开发系列教程三(消息体签名及加解密)

    http://www.cnblogs.com/zskbll/p/4139039.html C#微信公众号开发系列教程一(调试环境部署) C#微信公众号开发系列教程一(调试环境部署续:vs远程调试) C ...

  8. C#微信公众号开发系列教程五(接收事件推送与消息排重)

    C#微信公众号开发系列教程五(接收事件推送与消息排重) 原文:C#微信公众号开发系列教程五(接收事件推送与消息排重) 微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续 ...

  9. php公众号客服消息图文,微信公众号开发系列-发送客服消息(示例代码)

    下面是做微信公众号开发用到最多的两个客服消息发送类型,文本信息和图文信息. 1.发送文本消息{ "touser":"OPENID", "msgtype ...

最新文章

  1. 【安全系列之XSS】XSS攻击测试以及防御
  2. mysql性能优化-学习笔记
  3. 消息队列终极解决方案——Stream(下)
  4. SQL Server查询锁等待
  5. ubuntu怎么切换到root用户,切换到root账号方法
  6. 封装动态数组类Array
  7. java学到什么程度安卓_自学Android到什么程度才能找到工作?
  8. 【呆萌の研究】JavaScript常见的继承方式
  9. javascript毫秒计时器_JavaScript原生秒表、计时器
  10. MATLAB 超定方程组 最小二乘法
  11. ps 提示暂存满,不能导入文件
  12. 【华为二面】2020/3/25_华为第二次技术面试_45分钟
  13. Tigase8 SSL安全连接配置与代码实现
  14. 大学期间应当做的三件事
  15. 汇编中的OFFSET(OFFSET为属性操作符,表示应该把其后跟着的符号地址的值(不是内容)作为操作数)
  16. “熊孩子”乱敲键盘就攻破了Linux桌面,大神:17年前我就警告过你们
  17. 网络设备的连接以及配置
  18. php织梦源码安装,如何安装网上下载的织梦源码
  19. 态度篇·与其混不如滚
  20. 期权二叉树定价参数的计算

热门文章

  1. STM32-时钟系统详解
  2. Python相关基础信息
  3. 【Python养成】:案例(设计三维向量类、实现向量的加法、减法以及向量与标量的乘法和除法运算、编写自定义类,模拟内置集、编写自定义类,模拟双端队列。)
  4. SQL-建库、建表实例
  5. 如何利用织梦的内链功能,自动给文章加内链。
  6. 三分钟让你看懂共模电感如何选型?
  7. 浪漫的小王子是王德华
  8. HTML5为输入框添加语音输入功能的实现方法
  9. 成员函数对象类的const和非const成员函数的重载
  10. 玩转linux 这些命令就够了