diff --git a/README.md b/README.md index 5c129d52..0f63fcc1 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,26 @@ router.route(message); 3. 规则的结束必须用``Rule.end()``或者``Rule.next()``,否则不会生效 4. 具体使用可以看源代码中的``WxMessageRouterTest``单元测试,或者查看Javadoc +### 同步回复 + +``WxMessageRouter``默认使用异步的方式处理消息,如果要使用同步的方式处理消息,那么可以这样: + +```java +router + .rule() + .async(false) + .handler(handler) + .end() +; + +WxXmlMessage message = WxXmlMessage.fromXml(xml); +// 获得同步的返回结果 +WxXmlMessage res = router.route(message); +String xml = res.toXml(); +// ... 将xml写入HttpServletResponse +``` + + ## 微信Java API 使用``WxService``可以调用微信API。目前已实现除“微信小店”以外的所有功能。 diff --git a/src/main/java/chanjarster/weixin/api/WxMessageHandler.java b/src/main/java/chanjarster/weixin/api/WxMessageHandler.java index 7dcb7a77..e4f350e9 100644 --- a/src/main/java/chanjarster/weixin/api/WxMessageHandler.java +++ b/src/main/java/chanjarster/weixin/api/WxMessageHandler.java @@ -15,7 +15,8 @@ public interface WxMessageHandler { * * @param wxMessage * @param context 上下文,如果handler或interceptor之间有信息要传递,可以用这个 + * @return xml格式的消息,如果在异步规则里处理的话,可以返回null */ - public void handle(WxXmlMessage wxMessage, Map context); + public WxXmlMessage handle(WxXmlMessage wxMessage, Map context); } diff --git a/src/main/java/chanjarster/weixin/api/WxMessageRouter.java b/src/main/java/chanjarster/weixin/api/WxMessageRouter.java index 460853b5..02b2fb2f 100644 --- a/src/main/java/chanjarster/weixin/api/WxMessageRouter.java +++ b/src/main/java/chanjarster/weixin/api/WxMessageRouter.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import chanjarster.weixin.bean.WxXmlMessage; @@ -37,8 +39,10 @@ import chanjarster.weixin.bean.WxXmlMessage; */ public class WxMessageRouter { - private List rules = new ArrayList(); + private final List rules = new ArrayList(); + private final ExecutorService es = Executors.newCachedThreadPool(); + /** * 开始一个新的Route规则 * @return @@ -51,21 +55,52 @@ public class WxMessageRouter { * 处理微信消息 * @param wxMessage */ - public void route(WxXmlMessage wxMessage) { - for (Rule rule : rules) { + public WxXmlMessage route(final WxXmlMessage wxMessage) { + final List matchRules = new ArrayList(); + // 收集匹配的规则 + for (final Rule rule : rules) { if (rule.test(wxMessage)) { - rule.service(wxMessage); - if(!rule.reEnter) { - break; + matchRules.add(rule); + } + } + + if (matchRules.size() == 0) { + return null; + } + + if (matchRules.get(0).async) { + // 只要第一个是异步的,那就异步执行 + // 在另一个线程里执行 + es.submit(new Runnable() { + public void run() { + for (final Rule rule : matchRules) { + rule.service(wxMessage); + if (!rule.reEnter) { + break; + } + } } + }); + return null; + } + + WxXmlMessage res = null; + for (final Rule rule : matchRules) { + // 返回最后一个匹配规则的结果 + res = rule.service(wxMessage); + if (!rule.reEnter) { + break; } } + return res; } public static class Rule { private final WxMessageRouter routerBuilder; + private boolean async = true; + private String msgType; private String event; @@ -84,6 +119,16 @@ public class WxMessageRouter { this.routerBuilder = routerBuilder; } + /** + * 设置是否异步执行,默认是true + * @param async + * @return + */ + public Rule async(boolean async) { + this.async = async; + return this; + } + /** * 如果msgType等于某值 * @param msgType @@ -209,21 +254,22 @@ public class WxMessageRouter { * @param wxMessage * @return true 代表继续执行别的router,false 代表停止执行别的router */ - protected void service(WxXmlMessage wxMessage) { + protected WxXmlMessage service(WxXmlMessage wxMessage) { Map context = new HashMap(); // 如果拦截器不通过 for (WxMessageInterceptor interceptor : this.interceptors) { if (!interceptor.intercept(wxMessage, context)) { - return; + return null; } } // 交给handler处理 - for (WxMessageHandler interceptor : this.handlers) { - interceptor.handle(wxMessage, context); + WxXmlMessage res = null; + for (WxMessageHandler handler : this.handlers) { + // 返回最后handler的结果 + res = handler.handle(wxMessage, context); } - - return; + return res; } } diff --git a/src/test/java/chanjarster/weixin/api/WxMessageRouterTest.java b/src/test/java/chanjarster/weixin/api/WxMessageRouterTest.java index 944486db..22cc4264 100644 --- a/src/test/java/chanjarster/weixin/api/WxMessageRouterTest.java +++ b/src/test/java/chanjarster/weixin/api/WxMessageRouterTest.java @@ -3,7 +3,6 @@ package chanjarster.weixin.api; import java.util.Map; import org.testng.Assert; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -17,40 +16,78 @@ import chanjarster.weixin.bean.WxXmlMessage; @Test public class WxMessageRouterTest { - protected StringBuilder sb; - protected WxMessageRouter router; - - @BeforeMethod - public void prepare() { - this.sb = new StringBuilder(); - this.router = new WxMessageRouter(); + @Test(enabled = false) + public void prepare(boolean async, StringBuffer sb, WxMessageRouter router) { router .rule() + .async(async) .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1").content("CONTENT_1") .handler(new WxEchoMessageHandler(sb, "COMBINE_4")) .end() .rule() + .async(async) .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK).eventKey("KEY_1") .handler(new WxEchoMessageHandler(sb, "COMBINE_3")) .end() .rule() + .async(async) .msgType(WxConsts.XML_MSG_TEXT).event(WxConsts.EVT_CLICK) .handler(new WxEchoMessageHandler(sb, "COMBINE_2")) .end() - .rule().msgType(WxConsts.XML_MSG_TEXT).handler(new WxEchoMessageHandler(sb, WxConsts.XML_MSG_TEXT)).end() - .rule().event(WxConsts.EVT_CLICK).handler(new WxEchoMessageHandler(sb, WxConsts.EVT_CLICK)).end() - .rule().eventKey("KEY_1").handler(new WxEchoMessageHandler(sb, "KEY_1")).end() - .rule().content("CONTENT_1").handler(new WxEchoMessageHandler(sb, "CONTENT_1")).end() - .rule().handler(new WxEchoMessageHandler(sb, "ALL")).end(); + .rule().async(async).msgType(WxConsts.XML_MSG_TEXT).handler(new WxEchoMessageHandler(sb, WxConsts.XML_MSG_TEXT)).end() + .rule().async(async).event(WxConsts.EVT_CLICK).handler(new WxEchoMessageHandler(sb, WxConsts.EVT_CLICK)).end() + .rule().async(async).eventKey("KEY_1").handler(new WxEchoMessageHandler(sb, "KEY_1")).end() + .rule().async(async).content("CONTENT_1").handler(new WxEchoMessageHandler(sb, "CONTENT_1")).end() + .rule().async(async).handler(new WxEchoMessageHandler(sb, "ALL")).end(); ; } @Test(dataProvider="messages-1") - public void testSimple(WxXmlMessage message, String expected) { + public void testSync(WxXmlMessage message, String expected) { + StringBuffer sb = new StringBuffer(); + WxMessageRouter router = new WxMessageRouter(); + prepare(false, sb, router); + router.route(message); + Assert.assertEquals(sb.toString(), expected); + } + + @Test(dataProvider="messages-1") + public void testAsync(WxXmlMessage message, String expected) throws InterruptedException { + StringBuffer sb = new StringBuffer(); + WxMessageRouter router = new WxMessageRouter(); + prepare(true, sb, router); router.route(message); + Thread.sleep(500l); Assert.assertEquals(sb.toString(), expected); } + public void testConcurrency() throws InterruptedException { + final WxMessageRouter router = new WxMessageRouter(); + router.rule().handler(new WxMessageHandler() { + @Override + public WxXmlMessage handle(WxXmlMessage wxMessage, Map context) { + // TODO Auto-generated method stub + return null; + } + }).end(); + + final WxXmlMessage m = new WxXmlMessage(); + Runnable r = new Runnable() { + @Override + public void run() { + router.route(m); + try { + Thread.sleep(1000l); + } catch (InterruptedException e) { + } + } + }; + for (int i = 0; i < 10; i++) { + new Thread(r).start(); + } + + Thread.sleep(1000l * 2); + } @DataProvider(name="messages-1") public Object[][] messages2() { WxXmlMessage message1 = new WxXmlMessage(); @@ -98,17 +135,18 @@ public class WxMessageRouterTest { public static class WxEchoMessageHandler implements WxMessageHandler { - private StringBuilder sb; + private StringBuffer sb; private String echoStr; - public WxEchoMessageHandler(StringBuilder sb, String echoStr) { + public WxEchoMessageHandler(StringBuffer sb, String echoStr) { this.sb = sb; this.echoStr = echoStr; } @Override - public void handle(WxXmlMessage wxMessage, Map context) { + public WxXmlMessage handle(WxXmlMessage wxMessage, Map context) { sb.append(this.echoStr).append(','); + return null; } }