微信公眾平台消息類型繁多:
若用 if-else 或 switch 硬編碼分發,代碼膨脹、難維護、擴充新類型需改動核心控制器。
public interface WechatEventCenter {
BaseWechatMsg defaultEvent(); // 預設回覆
BaseWechatMsg text(); // 文本消息
BaseWechatMsg image();
BaseWechatMsg voice();
BaseWechatMsg video();
BaseWechatMsg location();
BaseWechatMsg link();
BaseWechatMsg event(); // 事件消息入口
BaseWechatMsg subscribe(); // 關注事件
BaseWechatMsg unsubscribe(); // 取消關注
BaseWechatMsg CLICK(); // 自定義菜單點擊事件(EventKey 即方法名)
BaseWechatMsg VIEW();
// ... 其他事件對應方法
}
@Controller
@RequestMapping("/wechat")
public class WechatController {
@Autowired private WechatEventCenter wechatEventCenter;
@RequestMapping(value = "index", produces = "text/plain;charset=utf-8")
@ResponseBody
public String index(HttpServletRequest request,
String signature, String timestamp, String nonce, String echostr) {
if ("GET".equals(request.getMethod())) {
return echostr; // 驗證簽名略
}
return doPost(request);
}
private String doPost(HttpServletRequest request) {
BaseWechatMsg replyMsg;
try {
// 1. 解析 XML -> Document
String xml = IOUtils.toString(request.getInputStream(), "UTF-8");
Document doc = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(new InputSource(new StringReader(xml)));
Element root = doc.getDocumentElement();
// 2. 註入解析後的 Document,供各處理方法取參
wechatEventCenter.setRoot(root);
// 3. 獲取 MsgType(text/image/voice/.../event)
String msgType = root.getElementsByTagName("MsgType").item(0).getTextContent();
// 4. 反射調用同名方法
Method method = wechatEventCenter.getClass().getMethod(msgType);
replyMsg = (BaseWechatMsg) method.invoke(wechatEventCenter);
} catch (NoSuchMethodException e) {
// 找不到對應方法,走預設
replyMsg = wechatEventCenter.defaultEvent();
} catch (Exception e) {
log.error("處理微信消息異常", e);
replyMsg = wechatEventCenter.defaultEvent();
}
return replyMsg.toXml();
}
}
// WechatEventCenter 實作類中
@Override
public BaseWechatMsg event() {
String event = root.getElementsByTagName("Event").item(0).getTextContent(); // subscribe/CLICK/...
try {
Method method = getClass().getMethod(event); // 事件類型即方法名
return (BaseWechatMsg) method.invoke(this);
} catch (NoSuchMethodException e) {
return defaultEvent();
}
}
| 維度 | if-else/switch | 反射分發 |
|---|---|---|
| 新增消息類型 | 修改核心控制器,違反 OCP | 僅在介面/實作類新增方法,零侵入 |
| 代碼結構 | 單一巨大方法,圈複雜度高 | 方法拆分細粒度,單一職責 |
| 測試性 | 難以單元測試各分支 | 每個方法可獨立測試 |
| 效能 | 極快 | 反射開銷微乎其微(毫秒級),可快取 Method 物件優化 |
Map,運行時直接 map.get(msgType) 避免 getMethod 反射查找。NoSuchMethodException 屬預期流程,不打印堆疊,僅 DEBUG 日誌。
src/main/java
├── controller
│ └── WechatController.java
├── core
│ ├── WechatEventCenter.java (介面)
│ └── WechatEventCenterImpl.java (實作)
├── entity
│ ├── BaseWechatMsg.java
│ ├── TextMsg.java
│ └── EventMsg.java
└── util
├── WechatUtils.java (XML 解析、簽名驗證)
└── MessageUtil.java (回覆 XML 組裝)