目录
- 准备工作:公众号与开发环境
- 1 注册并配置公众号
- 2 准备开发环境
- 核心概念:通信原理与消息格式
- 1 服务器配置与 Token 验证
- 2 消息推送机制
- 3 消息类型(XML 格式)
- 实战:搭建 Java 后端服务
- 1 创建 Spring Boot 项目
- 2 接入公众号(处理 Token 验证)
- 3 接收并解析用户消息
- 4 回复用户消息
- 进阶功能开发
- 1 获取 Access Token
- 2 菜单管理
- 3 发送模板消息
- 开发工具与调试
- 1 微信开发者工具
- 2 本地开发与穿透
- 完整代码示例
- 学习资源与官方文档
准备工作:公众号与开发环境
在开始编码之前,你需要完成以下准备工作。

1 注册并配置公众号
-
注册公众号:
- 访问 微信公众平台。
- 根据你的需求选择注册类型:
- 订阅号:适合个人或媒体,主要用于信息发布。
- 服务号:适合企业和组织,功能更强大(如微信支付、高级接口等)。
- 按照指引完成注册,并完成认证(认证后可获得更多接口权限)。
-
获取开发者凭证:
- 登录公众号后台,在 “设置与开发” -> “基本配置” 中找到开发者凭证。
- 你需要记录下 AppID (应用ID) 和 AppSecret (应用密钥),这两个是调用公众号 API 的钥匙。
-
获取服务器配置信息:
- 在 “设置与开发” -> “基本配置” 中,找到 “服务器配置”。
- 这里需要你填写:
- URL (服务器地址):你的 Java Web 服务的访问地址,必须以
http://或https://开头,并且能被外网访问。http://yourdomain.com/wechat。 - Token (令牌):可以任意填写,用于验证请求是否来自微信。
myWechatToken。 - EncodingAESKey (消息加解密密钥):可以随机生成,用于消息的加解密,初期开发可以先留空。
- URL (服务器地址):你的 Java Web 服务的访问地址,必须以
2 准备开发环境
- JDK:建议使用 JDK 8 或更高版本。
- IDE:IntelliJ IDEA 或 Eclipse。
- 构建工具:Maven 或 Gradle。
- Web 框架:推荐使用 Spring Boot,它能极大地简化 Web 应用的开发,本教程将基于 Spring Boot 2.x/3.x。
核心概念:通信原理与消息格式
理解微信和你的服务器是如何通信的,是开发的第一步。

1 服务器配置与 Token 验证
当你填写完服务器配置并点击“提交”时,微信服务器会向你的 URL 发送一个 GET 请求,用于验证你的服务器所有权,请求参数如下:
| 参数 | 描述 |
|---|---|
signature |
微信加密签名,结合了 token、timestamp、nonce |
timestamp |
时间戳 |
nonce |
随机数 |
echostr |
随机字符串 |
验证逻辑: 你的服务器需要做以下几件事:
- 获取
token(就是你填写的那个)、timestamp、nonce。 - 将这三个参数进行字典序排序。
- 将排序后的三个参数字符串拼接成一个字符串,并进行
SHA1加密。 - 将加密后的字符串与
signature进行比较。 - 如果一致,则说明请求来自微信,你需要将
echostr原样返回,如果不一致,则返回错误。
2 消息推送机制
当用户与公众号进行交互(如发送消息、点击菜单等)时,微信服务器会通过一个 POST 请求,将消息数据发送到你配置的 URL。
- 请求方式:
POST - 请求体,格式为 XML。
- Content-Type:
text/xml; charset=UTF-8
你的服务器需要解析这个 XML 请求,根据消息类型(文本、图片、事件等)进行处理,然后将处理结果以 XML 格式 响应给微信服务器,微信服务器再将你的响应推送给用户。

3 消息类型(XML 格式)
微信推送的消息有多种类型,每种类型都有其特定的 XML 结构。
文本消息 用户发送文本时,微信推送的结构如下:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[this is a test]]></Content> <MsgId>1234567890123456</MsgId> </xml>
事件推送 当用户关注、取消关注、点击菜单时,会收到事件推送,用户关注时:
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[subscribe]]></Event> </xml>
回复消息 当你回复用户时,也需要遵循 XML 格式,回复文本消息:
<xml> <ToUserName><![CDATA[fromUser]]></ToUserName> <FromUserName><![CDATA[toUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[你好,欢迎关注!]]></Content> </xml>
实战:搭建 Java 后端服务
我们开始用 Java 和 Spring Boot 来实现上述逻辑。
1 创建 Spring Boot 项目
使用 Spring Initializr 快速创建一个项目。
- Project: Maven
- Language: Java
- Spring Boot: 选择一个稳定版本 (如 2.7.x)
- Project Metadata:
- Group:
com.example - Artifact:
wechat-demo
- Group:
- Dependencies:
Spring Web: 用于创建 Web 应用和 RESTful 接口。
下载项目并用你的 IDE 打开。
2 接入公众号(处理 Token 验证)
创建一个 Controller 来处理来自微信的请求。
WeChatController.java
package com.example.wechatdemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@RestController
public class WeChatController {
// 在公众号后台配置的 Token
private final String TOKEN = "myWechatToken";
/**
* 处理微信服务器的 GET 请求(Token 验证)
*/
@GetMapping("/wechat")
public String validate(HttpServletRequest request, HttpServletResponse response) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
// 1. 将 token, timestamp, nonce 三个参数进行字典序排序
String[] arr = {TOKEN, timestamp, nonce};
Arrays.sort(arr);
// 2. 将三个参数字符串拼接成一个字符串
String content = arr[0] + arr[1] + arr[2];
// 3. 对拼接后的字符串进行 SHA1 加密
String tmpStr = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(content.getBytes());
tmpStr = bytesToHex(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 4. 将加密后的字符串与 signature 对比
if (tmpStr != null && tmpStr.equals(signature)) {
// 验证成功,原样返回 echostr
return echostr;
} else {
return "error";
}
}
// 字节数组转十六进制字符串
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
// ... 后续会添加处理 POST 请求的方法
}
测试:
将你的项目运行起来,获取到外网可访问的地址(可以使用 ngrok 等工具进行内网穿透),将这个地址和你在 1.1 中设置的 TOKEN 填入公众号后台的“服务器配置”中,点击“提交”,如果配置成功,说明你的服务器已经成功接入公众号。
3 接收并解析用户消息
现在我们来处理用户发送的 POST 请求,由于请求是 XML 格式,我们需要一个 XML 解析库,这里推荐使用 dom4j。
添加依赖
在 pom.xml 中添加 dom4j:
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.4</version>
</dependency>
创建消息实体类 为了方便处理 XML,我们先创建一些 Java 类来映射 XML 结构。
BaseMessage.java (基类)
package com.example.wechatdemo.model;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class BaseMessage {
protected String ToUserName;
protected String FromUserName;
protected long CreateTime;
protected String MsgType;
// Getters and Setters
public String getToUserName() { return ToUserName; }
public void setToUserName(String toUserName) { ToUserName = toUserName; }
public String getFromUserName() { return FromUserName; }
public void setFromUserName(String fromUserName) { FromUserName = fromUserName; }
public long getCreateTime() { return CreateTime; }
public void setCreateTime(long createTime) { CreateTime = createTime; }
public String getMsgType() { return MsgType; }
public void setMsgType(String msgType) { MsgType = msgType; }
}
TextMessage.java (文本消息)
package com.example.wechatdemo.model;
public class TextMessage extends BaseMessage {
private String Content;
// Getter and Setter
public String getContent() { return Content; }
public void setContent(String content) { Content = content; }
}
EventMessage.java (事件消息)
package com.example.wechatdemo.model;
public class EventMessage extends BaseMessage {
private String Event;
// Getter and Setter
public String getEvent() { return Event; }
public void setEvent(String event) { Event = event; }
}
解析 XML 并处理
修改 WeChatController,添加 POST 方法。
WeChatController.java (更新)
package com.example.wechatdemo.controller;
// ... imports ...
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.wechatdemo.model.TextMessage;
import com.example.wechatdemo.model.EventMessage;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
@RestController
public class WeChatController {
// ... 前面的 GET 方法保持不变 ...
/**
* 处理微信服务器的 POST 请求(接收用户消息)
*/
@PostMapping("/wechat")
public String handlePost(HttpServletRequest request, HttpServletResponse response) throws IOException, DocumentException {
// 1. 获取请求输入流(XML 格式)
InputStream is = request.getInputStream();
// 2. 解析 XML
SAXReader reader = new SAXReader();
Document document = reader.read(is);
Element root = document.getRootElement();
// 3. 获取消息类型
String msgType = root.element("MsgType").getTextTrim();
// 4. 根据消息类型进行处理
if ("text".equals(msgType)) {
// 处理文本消息
TextMessage textMessage = parseTextMessage(root);
return handleTextMessage(textMessage);
} else if ("event".equals(msgType)) {
// 处理事件消息
EventMessage eventMessage = parseEventMessage(root);
return handleEventMessage(eventMessage);
}
return ""; // 其他消息类型暂时不处理
}
// 解析文本消息
private TextMessage parseTextMessage(Element root) {
TextMessage msg = new TextMessage();
msg.setToUserName(root.element("ToUserName").getTextTrim());
msg.setFromUserName(root.element("FromUserName").getTextTrim());
msg.setCreateTime(Long.parseLong(root.element("CreateTime").getTextTrim()));
msg.setMsgType(root.element("MsgType").getTextTrim());
msg.setContent(root.element("Content").getTextTrim());
return msg;
}
// 解析事件消息
private EventMessage parseEventMessage(Element root) {
EventMessage msg = new EventMessage();
msg.setToUserName(root.element("ToUserName").getTextTrim());
msg.setFromUserName(root.element("FromUserName").getTextTrim());
msg.setCreateTime(Long.parseLong(root.element("CreateTime").getTextTrim()));
msg.setMsgType(root.element("MsgType").getTextTrim());
msg.setEvent(root.element("Event").getTextTrim());
return msg;
}
// 处理文本消息并返回回复
private String handleTextMessage(TextMessage receivedMsg) {
// 1. 构造回复消息
TextMessage replyMsg = new TextMessage();
replyMsg.setToUserName(receivedMsg.getFromUserName()); // 发送方和接收方互换
replyMsg.setFromUserName(receivedMsg.getToUserName());
replyMsg.setCreateTime(System.currentTimeMillis() / 1000);
replyMsg.setMsgType("text");
// 2. 设置回复内容(简单 echo)
String content = "你发送的是: " + receivedMsg.getContent();
replyMsg.setContent(content);
// 3. 将对象转换为 XML 字符串
return convertToXml(replyMsg);
}
// 处理事件消息
private String handleEventMessage(EventMessage eventMsg) {
if ("subscribe".equals(eventMsg.getEvent())) {
// 用户关注事件
TextMessage replyMsg = new TextMessage();
replyMsg.setToUserName(eventMsg.getFromUserName());
replyMsg.setFromUserName(eventMsg.getToUserName());
replyMsg.setCreateTime(System.currentTimeMillis() / 1000);
replyMsg.setMsgType("text");
replyMsg.setContent("感谢关注!这是一个 Java 开发的测试公众号。");
return convertToXml(replyMsg);
}
return "";
}
// 将消息对象转换为 XML 字符串(简化版,实际项目中建议使用 JAXB)
private String convertToXml(BaseMessage message) {
// 这里为了简化,直接拼接字符串,生产环境请使用 JAXB 或其他 XML 绑定库。
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
sb.append("<ToUserName><![CDATA[").append(message.getToUserName()).append("]]></ToUserName>");
sb.append("<FromUserName><![CDATA[").append(message.getFromUserName()).append("]]></FromUserName>");
sb.append("<CreateTime>").append(message.getCreateTime()).append("</CreateTime>");
sb.append("<MsgType><![CDATA[").append(message.getMsgType()).append("]]></MsgType>");
if (message instanceof TextMessage) {
sb.append("<Content><![CDATA[").append(((TextMessage) message).getContent()).append("]]></Content>");
}
sb.append("</xml>");
return sb.toString();
}
}
你的公众号已经可以接收文本消息并回复,并且在用户关注时自动欢迎了。
进阶功能开发
1 获取 Access Token
调用绝大多数公众号 API(如创建菜单、发送模板消息)都需要一个全局唯一的接口调用凭据——access_token。
- 获取方式:通过 GET 请求
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET。 - 特点:
- 有效期为 2 小时(7200秒)。
- 每天获取次数有限。
- 必须全局缓存,不能每次调用都去请求,否则会触发频率限制。
实现一个 Access Token 服务:
AccessTokenService.java
package com.example.wechatdemo.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.TimeUnit;
@Service
public class AccessTokenService {
@Value("${wechat.appid}")
private String appId;
@Value("${wechat.secret}")
private String appSecret;
private String accessToken;
private long expireTime;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public AccessTokenService(RestTemplate restTemplate, ObjectMapper objectMapper) {
this.restTemplate = restTemplate;
this.objectMapper = objectMapper;
}
public String getAccessToken() {
// token 为空或已过期,则重新获取
if (accessToken == null || System.currentTimeMillis() > expireTime) {
String url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, appSecret);
String response = restTemplate.getForObject(url, String.class);
try {
JsonNode root = objectMapper.readTree(response);
this.accessToken = root.get("access_token").asText();
// 设置过期时间(提前 5 分钟刷新)
this.expireTime = System.currentTimeMillis() + (root.get("expires_in").asLong() - 300) * 1000;
} catch (Exception e) {
// 处理异常
e.printStackTrace();
}
}
return accessToken;
}
}
在 application.properties 中配置 AppID 和 Secret:
wechat.appid=你的AppID wechat.secret=你的AppSecret
2 菜单管理
通过 API 可以自定义公众号的菜单。
创建菜单按钮的 JSON 结构
{
"button": [
{
"type": "click",
"name": "今日歌曲",
"key": "V1001_TODAY_MUSIC"
},
{
"name": "菜单",
"sub_button": [
{
"type": "view",
"name": "搜索",
"url": "http://www.soso.com/"
},
{
"type": "click",
"name": "赞一下我们",
"key": "V1001_GOOD"
}
]
}
]
}
调用 API 创建菜单
在 AccessTokenService 中添加方法:
public void createMenu(String menuJson) {
String url = String.format("https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s", getAccessToken());
restTemplate.postForObject(url, menuJson, String.class);
}
然后你可以在你的业务逻辑中调用这个方法来创建菜单。
3 发送模板消息
模板消息用于在用户完成特定操作后,向用户发送重要通知。
获取模板 ID 在公众号后台“模板消息”中找到你需要的模板,并记录其 ID。
调用 API 发送
发送模板消息需要接收用户的 openid。
public void sendTemplateMessage(String openid, String templateId, String url, Map<String, String> data) {
String accessToken = getAccessToken();
String requestUrl = String.format("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s", accessToken);
// 构建请求 JSON
Map<String, Object> message = new HashMap<>();
message.put("touser", openid);
message.put("template_id", templateId);
if (url != null) {
message.put("url", url);
}
message.put("data", data);
// 发送请求
restTemplate.postForObject(requestUrl, message, String.class);
}
开发工具与调试
1 微信开发者工具
官方提供的桌面工具,非常强大。
- 模拟器:可以模拟手机界面,查看你的公众号效果。
- 调试:可以查看网络请求、Console 日志,方便排查问题。
- 素材管理:可以预览图片、视频等素材。
2 本地开发与穿透
在开发阶段,你的本地电脑通常无法被外网访问,这时需要使用内网穿透工具,将本地的端口映射到一个公网 URL。
完整代码示例
本教程的完整代码已托管在 GitHub 上,你可以参考它来更好地理解: https://github.com/your-github-username/wechat-java-demo (请替换为你的实际地址)
学习资源与官方文档
- 微信公众平台技术文档:这是最重要的资源,所有 API 规则和消息格式都在这里。
- Java 微信 SDK:为了避免重复造轮子,可以考虑使用成熟的第三方 SDK,它们封装了大量的 API 和消息处理逻辑。
- me.chanjar:weixin-java-mp: 一个非常流行和功能全面的 Java 微信公众号 SDK。
- WxJava: 另一个优秀的、支持微信所有平台(公众号、小程序、企业微信等)的 Java 开发框架。
微信公众号开发的核心在于:
- 理解通信流程:GET 验证,POST 交互。
- 处理 XML 消息:能正确解析和构造微信的 XML 格式数据。
- 善用 Access Token:缓存它,避免频繁请求。
- 参考官方文档:遇到任何 API 问题,第一时间查阅官方文档。
希望这份教程能帮助你顺利开启 Java 微信公众号开发之旅!祝你编码愉快!
