From 80c794b8aee14ea65dd905728b3399d983bbeea9 Mon Sep 17 00:00:00 2001 From: jinyu Date: Tue, 12 Apr 2016 16:39:08 +0800 Subject: [PATCH] up --- .../Weixin4jServerStartupWithoutThread.java | 104 +-- .../startup/WeixinServerBootstrap.java | 640 +++++++++--------- 2 files changed, 362 insertions(+), 382 deletions(-) diff --git a/weixin4j-example/src/main/java/com/foximy/weixin4j/example/server/Weixin4jServerStartupWithoutThread.java b/weixin4j-example/src/main/java/com/foximy/weixin4j/example/server/Weixin4jServerStartupWithoutThread.java index c46c873e..fa1b79ef 100644 --- a/weixin4j-example/src/main/java/com/foximy/weixin4j/example/server/Weixin4jServerStartupWithoutThread.java +++ b/weixin4j-example/src/main/java/com/foximy/weixin4j/example/server/Weixin4jServerStartupWithoutThread.java @@ -1,52 +1,52 @@ -package com.foximy.weixin4j.example.server; - -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.spring.SpringBeanFactory; -import com.foxinmy.weixin4j.startup.WeixinServerBootstrap; - -/** - * 微信消息服务:单独作为一个服务jar包启动 - * - * @className Weixin4jServerStartupWithoutThread - * @author jy - * @date 2015年5月7日 - * @since JDK 1.7 - * @see - */ -public class Weixin4jServerStartupWithoutThread { - /** - * 服务监听的端口号,目前微信只支持80端口,可以考虑用nginx做转发到此端口 - */ - private static int port = 10000; - /** - * 服务器token信息 - */ - /** - * 明文模式:String aesToken = ""; 密文模式:AesToken aesToken = new - * AesToken("公众号appid", "公众号token","公众号加密/解密消息的密钥"); - */ - private static String aesToken = "weixin4j"; - /** - * 处理微信消息的全限包名(也可通过addHandler方式一个一个添加) - */ - private static String handlerPackage = "com.foximy.weixin4j.example.server.handler"; - - /** - * 入口函数 可使用assembly插件打成可执行zip包:https://github.com/foxinmy/weixin4j/wiki/ - * assembly%E6%89%93%E5%8C%85 - * - * @param args - * @throws WeixinException - */ - public static void main(String[] args) throws WeixinException { - ApplicationContext applicationContext = new ClassPathXmlApplicationContext( - new String[] { "classpath:/spring-bean.xml" }); - new WeixinServerBootstrap(aesToken) - .handlerPackagesToScan(handlerPackage).openAlwaysResponse() - .resolveBeanFactory(new SpringBeanFactory(applicationContext)) - .startup(port); - } -} +package com.foximy.weixin4j.example.server; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import com.foxinmy.weixin4j.exception.WeixinException; +import com.foxinmy.weixin4j.spring.SpringBeanFactory; +import com.foxinmy.weixin4j.startup.WeixinServerBootstrap; + +/** + * 微信消息服务:单独作为一个服务jar包启动 + * + * @className Weixin4jServerStartupWithoutThread + * @author jy + * @date 2015年5月7日 + * @since JDK 1.7 + * @see + */ +public class Weixin4jServerStartupWithoutThread { + /** + * 服务监听的端口号,目前微信只支持80端口,可以考虑用nginx做转发到此端口 + */ + private static int port = 30000; + /** + * 服务器token信息 + */ + /** + * 明文模式:String aesToken = ""; 密文模式:AesToken aesToken = new + * AesToken("公众号appid", "公众号token","公众号加密/解密消息的密钥"); + */ + private static String aesToken = "weixin4j"; + /** + * 处理微信消息的全限包名(也可通过addHandler方式一个一个添加) + */ + private static String handlerPackage = "com.foximy.weixin4j.example.server.handler"; + + /** + * 入口函数 可使用assembly插件打成可执行zip包:https://github.com/foxinmy/weixin4j/wiki/ + * assembly%E6%89%93%E5%8C%85 + * + * @param args + * @throws WeixinException + */ + public static void main(String[] args) throws WeixinException { + ApplicationContext applicationContext = new ClassPathXmlApplicationContext( + new String[] { "classpath:/spring-bean.xml" }); + new WeixinServerBootstrap(aesToken) + .handlerPackagesToScan(handlerPackage).openAlwaysResponse() + .resolveBeanFactory(new SpringBeanFactory(applicationContext)) + .startup(port); + } +} diff --git a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/startup/WeixinServerBootstrap.java b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/startup/WeixinServerBootstrap.java index 0f5c2bee..5b2795b0 100644 --- a/weixin4j-server/src/main/java/com/foxinmy/weixin4j/startup/WeixinServerBootstrap.java +++ b/weixin4j-server/src/main/java/com/foxinmy/weixin4j/startup/WeixinServerBootstrap.java @@ -1,331 +1,311 @@ -package com.foxinmy.weixin4j.startup; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.FutureListener; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.foxinmy.weixin4j.dispatcher.BeanFactory; -import com.foxinmy.weixin4j.dispatcher.DefaultMessageMatcher; -import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher; -import com.foxinmy.weixin4j.dispatcher.WeixinMessageKey; -import com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher; -import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.handler.WeixinMessageHandler; -import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor; -import com.foxinmy.weixin4j.request.WeixinMessage; -import com.foxinmy.weixin4j.socket.WeixinServerInitializer; -import com.foxinmy.weixin4j.util.AesToken; - -/** - * 微信netty服务启动程序 - * - * @className WeixinServerBootstrap - * @author jy - * @date 2014年10月12日 - * @since JDK 1.6 - * @see com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher - * @see com.foxinmy.weixin4j.handler.WeixinMessageHandler - * @see com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor - * @see com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher - * @see com.foxinmy.weixin4j.dispatcher.BeanFactory - */ -public final class WeixinServerBootstrap { - - private final InternalLogger logger = InternalLoggerFactory - .getInstance(getClass()); - - /** - * boss线程数,默认设置为cpu的核数 - */ - public final static int DEFAULT_BOSSTHREADS; - /** - * worker线程数,默认设置为DEFAULT_BOSSTHREADS * 4 - */ - public final static int DEFAULT_WORKERTHREADS; - /** - * 服务启动的默认端口 - */ - public final static int DEFAULT_SERVERPORT = 30000; - /** - * 消息分发器 - */ - private WeixinMessageDispatcher messageDispatcher; - /** - * 消息处理器 - */ - private List messageHandlerList; - /** - * 消息拦截器 - */ - private List messageInterceptorList; - - /** - * aes and token - * - */ - private final Map aesTokenMap; - - static { - DEFAULT_BOSSTHREADS = Runtime.getRuntime().availableProcessors(); - DEFAULT_WORKERTHREADS = DEFAULT_BOSSTHREADS * 4; - } - - /** - * - * 明文模式 - * - * @param token - * 开发者token - * - */ - public WeixinServerBootstrap(String token) { - this(null, token, null); - } - - /** - * 明文模式 & 兼容模式 & 密文模式 - * - * @param weixinId - * 公众号的应用ID(appid/corpid) 密文&兼容模式下需要填写 - * - * @param token - * 开发者填写的token 无论哪种模式都需要填写 - * @param aesKey - * 消息加密的密钥 密文&兼容模式下需要填写 - */ - public WeixinServerBootstrap(String weixinId, String token, String aesKey) { - this(new AesToken(weixinId, token, aesKey)); - } - - /** - * 多个公众号的支持 - *

- * 请注意:需在服务接收事件的URL中附加一个名为wexin_id的参数,其值请填写公众号的appid/ - * corpid - *

- * - * @param aesTokens - * 多个公众号 - * @return - */ - public WeixinServerBootstrap(AesToken... aesToken) { - this(new DefaultMessageMatcher(), aesToken); - } - - /** - * 多个公众号的支持 - *

- * 请注意:需在服务接收事件的URL中附加一个名为wexin_id的参数,其值请填写公众号的appid/ - * corpid - *

- * - * @param messageMatcher - * 消息匹配器 - * @param aesTokens - * 公众号信息 - * @return - */ - public WeixinServerBootstrap(WeixinMessageMatcher messageMatcher, - AesToken... aesTokens) { - if (messageMatcher == null) { - throw new IllegalArgumentException("MessageMatcher not be null"); - } - if (aesTokens == null) { - throw new IllegalArgumentException("AesToken not be null"); - } - this.aesTokenMap = new HashMap(); - for (AesToken aesToken : aesTokens) { - this.aesTokenMap.put(aesToken.getWeixinId(), aesToken); - } - this.aesTokenMap.put(null, aesTokens[0]); - this.messageHandlerList = new ArrayList(); - this.messageInterceptorList = new ArrayList(); - this.messageDispatcher = new WeixinMessageDispatcher(messageMatcher); - } - - /** - * 默认端口启动服务 - * - */ - public void startup() throws WeixinException { - startup(DEFAULT_SERVERPORT); - } - - /** - * 指定端口启动服务 - * - */ - public void startup(int serverPort) throws WeixinException { - startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort); - } - - /** - * 接受参数启动服务 - * - * @param bossThreads - * boss线程数 - * @param workerThreads - * worker线程数 - * @param serverPort - * 服务启动端口 - * @return - * @throws WeixinException - */ - public void startup(int bossThreads, int workerThreads, final int serverPort) - throws WeixinException { - messageDispatcher.setMessageHandlerList(messageHandlerList); - messageDispatcher.setMessageInterceptorList(messageInterceptorList); - - EventLoopGroup bossGroup = new NioEventLoopGroup(bossThreads); - EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads); - try { - ServerBootstrap b = new ServerBootstrap(); - b.option(ChannelOption.SO_BACKLOG, 1024); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler()) - .childHandler( - new WeixinServerInitializer(aesTokenMap, - messageDispatcher)); - Channel ch = b.bind(serverPort) - .addListener(new FutureListener() { - @Override - public void operationComplete(Future future) - throws Exception { - if (future.isSuccess()) { - logger.info("weixin4j server startup OK:{}", - serverPort); - } else { - logger.info("weixin4j server startup FAIL:{}", - serverPort); - } - } - }).sync().channel(); - ch.closeFuture().sync(); - } catch (InterruptedException e) { - throw new WeixinException(e); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } - - /** - * 添加一个或者多个消息处理器 - * - * @param messageHandler - * 消息处理器 - * @return - */ - public WeixinServerBootstrap addHandler( - WeixinMessageHandler... messageHandler) { - if (messageHandler == null) { - throw new IllegalArgumentException("messageHandler not be null"); - } - messageHandlerList.addAll(Arrays.asList(messageHandler)); - return this; - } - - /** - * 插入一个或多个消息拦截器 - * - * @param messageInterceptor - * 消息拦截器 - * @return - */ - public WeixinServerBootstrap addInterceptor( - WeixinMessageInterceptor... messageInterceptor) { - if (messageInterceptor == null) { - throw new IllegalArgumentException("messageInterceptor not be null"); - } - messageInterceptorList.addAll(Arrays.asList(messageInterceptor)); - return this; - } - - /** - * 按照包名去添加消息处理器 - * - * @param messageHandlerPackages - * 消息处理器所在的包名 - * @return - */ - public WeixinServerBootstrap handlerPackagesToScan( - String... messageHandlerPackages) { - if (messageHandlerPackages == null) { - throw new IllegalArgumentException( - "messageHandlerPackages not be null"); - } - messageDispatcher.setMessageHandlerPackages(messageHandlerPackages); - return this; - } - - /** - * 按照包名去添加消息拦截器 - * - * @param messageInterceptorPackages - * 消息拦截器所在的包名 - * @return - */ - public WeixinServerBootstrap interceptorPackagesToScan( - String... messageInterceptorPackages) { - if (messageInterceptorPackages == null) { - throw new IllegalArgumentException( - "messageInterceptorPackages not be null"); - } - messageDispatcher - .setMessageInterceptorPackages(messageInterceptorPackages); - return this; - } - - /** - * 声明处理器跟拦截器类实例化的构造工厂,否则通过Class.newInstance的方式构造 - * - * @param beanFactory - * Bean构造工厂 - * @return - */ - public WeixinServerBootstrap resolveBeanFactory(BeanFactory beanFactory) { - messageDispatcher.setBeanFactory(beanFactory); - return this; - } - - /** - * 注册消息类型 - * - * @param messageKey - * 消息key - * @param messageClass - * 消息类 - * @return - */ - public WeixinServerBootstrap registMessageClass( - WeixinMessageKey messageKey, - Class messageClass) { - messageDispatcher.registMessageClass(messageKey, messageClass); - return this; - } - - /** - * 打开总是响应开关,如未匹配到MessageHandler时回复空白消息 - */ - public WeixinServerBootstrap openAlwaysResponse() { - messageDispatcher.openAlwaysResponse(); - return this; - } - - public final static String VERSION = "1.1.7"; +package com.foxinmy.weixin4j.startup; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.FutureListener; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.foxinmy.weixin4j.dispatcher.BeanFactory; +import com.foxinmy.weixin4j.dispatcher.DefaultMessageMatcher; +import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher; +import com.foxinmy.weixin4j.dispatcher.WeixinMessageKey; +import com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher; +import com.foxinmy.weixin4j.exception.WeixinException; +import com.foxinmy.weixin4j.handler.WeixinMessageHandler; +import com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor; +import com.foxinmy.weixin4j.request.WeixinMessage; +import com.foxinmy.weixin4j.socket.WeixinServerInitializer; +import com.foxinmy.weixin4j.util.AesToken; + +/** + * 微信netty服务启动程序 + * + * @className WeixinServerBootstrap + * @author jy + * @date 2014年10月12日 + * @since JDK 1.6 + * @see com.foxinmy.weixin4j.dispatcher.WeixinMessageMatcher + * @see com.foxinmy.weixin4j.handler.WeixinMessageHandler + * @see com.foxinmy.weixin4j.interceptor.WeixinMessageInterceptor + * @see com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher + * @see com.foxinmy.weixin4j.dispatcher.BeanFactory + */ +public final class WeixinServerBootstrap { + + private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass()); + + /** + * boss线程数,默认设置为cpu的核数 + */ + public final static int DEFAULT_BOSSTHREADS; + /** + * worker线程数,默认设置为DEFAULT_BOSSTHREADS * 4 + */ + public final static int DEFAULT_WORKERTHREADS; + /** + * 服务启动的默认端口 + */ + public final static int DEFAULT_SERVERPORT = 30000; + /** + * 消息分发器 + */ + private WeixinMessageDispatcher messageDispatcher; + /** + * 消息处理器 + */ + private List messageHandlerList; + /** + * 消息拦截器 + */ + private List messageInterceptorList; + + /** + * aes and token + * + */ + private final Map aesTokenMap; + + static { + DEFAULT_BOSSTHREADS = Runtime.getRuntime().availableProcessors(); + DEFAULT_WORKERTHREADS = DEFAULT_BOSSTHREADS * 4; + } + + /** + * + * 明文模式 + * + * @param token + * 开发者token + * + */ + public WeixinServerBootstrap(String token) { + this(null, token, null); + } + + /** + * 明文模式 & 兼容模式 & 密文模式 + *

值得注意的是:企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数
+ * + * @param weixinId + * 公众号的应用ID(appid/corpid) 密文&兼容模式下需要填写 + * + * @param token + * 开发者填写的token 无论哪种模式都需要填写 + * @param aesKey + * 消息加密的密钥 密文&兼容模式下需要填写 + */ + public WeixinServerBootstrap(String weixinId, String token, String aesKey) { + this(new AesToken(weixinId, token, aesKey)); + } + + /** + * 多个公众号的支持 + *
值得注意的是: + *
1).企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数
+ *
2).非明文模式下需要在服务器URL后面加多一个`weixin_id={对应的appid/corpid}`的参数
+ * + * @param aesTokens + * 多个公众号 + * @return + */ + public WeixinServerBootstrap(AesToken... aesToken) { + this(new DefaultMessageMatcher(), aesToken); + } + + /** + * 多个公众号的支持 + *
值得注意的是: + *
1).企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数
+ *
2).非明文模式下需要在服务器URL后面加多一个`weixin_id={对应的appid/corpid}`的参数
+ * + * @param messageMatcher + * 消息匹配器 + * @param aesTokens + * 公众号信息 + * @return + */ + public WeixinServerBootstrap(WeixinMessageMatcher messageMatcher, AesToken... aesTokens) { + if (messageMatcher == null) { + throw new IllegalArgumentException("MessageMatcher not be null"); + } + if (aesTokens == null) { + throw new IllegalArgumentException("AesToken not be null"); + } + this.aesTokenMap = new HashMap(); + for (AesToken aesToken : aesTokens) { + this.aesTokenMap.put(aesToken.getWeixinId(), aesToken); + } + this.aesTokenMap.put(null, aesTokens[0]); + this.messageHandlerList = new ArrayList(); + this.messageInterceptorList = new ArrayList(); + this.messageDispatcher = new WeixinMessageDispatcher(messageMatcher); + } + + /** + * 默认端口(30000)启动服务 + * + */ + public void startup() throws WeixinException { + startup(DEFAULT_SERVERPORT); + } + + /** + * 指定端口启动服务 + * + */ + public void startup(int serverPort) throws WeixinException { + startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort); + } + + /** + * 接受参数启动服务 + * + * @param bossThreads + * boss线程数 + * @param workerThreads + * worker线程数 + * @param serverPort + * 服务启动端口 + * @return + * @throws WeixinException + */ + public void startup(int bossThreads, int workerThreads, final int serverPort) throws WeixinException { + messageDispatcher.setMessageHandlerList(messageHandlerList); + messageDispatcher.setMessageInterceptorList(messageInterceptorList); + + EventLoopGroup bossGroup = new NioEventLoopGroup(bossThreads); + EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads); + try { + ServerBootstrap b = new ServerBootstrap(); + b.option(ChannelOption.SO_BACKLOG, 1024); + b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler()) + .childHandler(new WeixinServerInitializer(aesTokenMap, messageDispatcher)); + Channel ch = b.bind(serverPort).addListener(new FutureListener() { + @Override + public void operationComplete(Future future) throws Exception { + if (future.isSuccess()) { + logger.info("weixin4j server startup OK:{}", serverPort); + } else { + logger.info("weixin4j server startup FAIL:{}", serverPort); + } + } + }).sync().channel(); + ch.closeFuture().sync(); + } catch (InterruptedException e) { + throw new WeixinException("netty server startup FAIL", e); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } + + /** + * 添加一个或者多个消息处理器 + * + * @param messageHandler + * 消息处理器 + * @return + */ + public WeixinServerBootstrap addHandler(WeixinMessageHandler... messageHandler) { + if (messageHandler == null) { + throw new IllegalArgumentException("messageHandler not be null"); + } + messageHandlerList.addAll(Arrays.asList(messageHandler)); + return this; + } + + /** + * 插入一个或多个消息拦截器 + * + * @param messageInterceptor + * 消息拦截器 + * @return + */ + public WeixinServerBootstrap addInterceptor(WeixinMessageInterceptor... messageInterceptor) { + if (messageInterceptor == null) { + throw new IllegalArgumentException("messageInterceptor not be null"); + } + messageInterceptorList.addAll(Arrays.asList(messageInterceptor)); + return this; + } + + /** + * 按照包名去添加消息处理器 + * + * @param messageHandlerPackages + * 消息处理器所在的包名 + * @return + */ + public WeixinServerBootstrap handlerPackagesToScan(String... messageHandlerPackages) { + if (messageHandlerPackages == null) { + throw new IllegalArgumentException("messageHandlerPackages not be null"); + } + messageDispatcher.setMessageHandlerPackages(messageHandlerPackages); + return this; + } + + /** + * 按照包名去添加消息拦截器 + * + * @param messageInterceptorPackages + * 消息拦截器所在的包名 + * @return + */ + public WeixinServerBootstrap interceptorPackagesToScan(String... messageInterceptorPackages) { + if (messageInterceptorPackages == null) { + throw new IllegalArgumentException("messageInterceptorPackages not be null"); + } + messageDispatcher.setMessageInterceptorPackages(messageInterceptorPackages); + return this; + } + + /** + * 声明处理器跟拦截器类实例化的构造工厂,否则通过Class.newInstance的方式构造 + * + * @param beanFactory + * Bean构造工厂 + * @return + */ + public WeixinServerBootstrap resolveBeanFactory(BeanFactory beanFactory) { + messageDispatcher.setBeanFactory(beanFactory); + return this; + } + + /** + * 注册消息类型 + * + * @param messageKey + * 消息key + * @param messageClass + * 消息类 + * @return + */ + public WeixinServerBootstrap registMessageClass(WeixinMessageKey messageKey, + Class messageClass) { + messageDispatcher.registMessageClass(messageKey, messageClass); + return this; + } + + /** + * 打开总是响应开关,如未匹配到MessageHandler时回复空白消息 + */ + public WeixinServerBootstrap openAlwaysResponse() { + messageDispatcher.openAlwaysResponse(); + return this; + } + + public final static String VERSION = "1.1.7"; } \ No newline at end of file