This commit is contained in:
jinyu 2016-04-12 16:39:08 +08:00
parent 5b7ed61713
commit 80c794b8ae
2 changed files with 362 additions and 382 deletions

View File

@ -1,52 +1,52 @@
package com.foximy.weixin4j.example.server; package com.foximy.weixin4j.example.server;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.exception.WeixinException;
import com.foxinmy.weixin4j.spring.SpringBeanFactory; import com.foxinmy.weixin4j.spring.SpringBeanFactory;
import com.foxinmy.weixin4j.startup.WeixinServerBootstrap; import com.foxinmy.weixin4j.startup.WeixinServerBootstrap;
/** /**
* 微信消息服务:单独作为一个服务jar包启动 * 微信消息服务:单独作为一个服务jar包启动
* *
* @className Weixin4jServerStartupWithoutThread * @className Weixin4jServerStartupWithoutThread
* @author jy * @author jy
* @date 2015年5月7日 * @date 2015年5月7日
* @since JDK 1.7 * @since JDK 1.7
* @see * @see
*/ */
public class Weixin4jServerStartupWithoutThread { public class Weixin4jServerStartupWithoutThread {
/** /**
* 服务监听的端口号,目前微信只支持80端口,可以考虑用nginx做转发到此端口 * 服务监听的端口号,目前微信只支持80端口,可以考虑用nginx做转发到此端口
*/ */
private static int port = 10000; private static int port = 30000;
/** /**
* 服务器token信息 * 服务器token信息
*/ */
/** /**
* 明文模式:String aesToken = ""; 密文模式:AesToken aesToken = new * 明文模式:String aesToken = ""; 密文模式:AesToken aesToken = new
* AesToken("公众号appid", "公众号token","公众号加密/解密消息的密钥"); * AesToken("公众号appid", "公众号token","公众号加密/解密消息的密钥");
*/ */
private static String aesToken = "weixin4j"; private static String aesToken = "weixin4j";
/** /**
* 处理微信消息的全限包名(也可通过addHandler方式一个一个添加) * 处理微信消息的全限包名(也可通过addHandler方式一个一个添加)
*/ */
private static String handlerPackage = "com.foximy.weixin4j.example.server.handler"; private static String handlerPackage = "com.foximy.weixin4j.example.server.handler";
/** /**
* 入口函数 可使用assembly插件打成可执行zip包:https://github.com/foxinmy/weixin4j/wiki/ * 入口函数 可使用assembly插件打成可执行zip包:https://github.com/foxinmy/weixin4j/wiki/
* assembly%E6%89%93%E5%8C%85 * assembly%E6%89%93%E5%8C%85
* *
* @param args * @param args
* @throws WeixinException * @throws WeixinException
*/ */
public static void main(String[] args) throws WeixinException { public static void main(String[] args) throws WeixinException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext( ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
new String[] { "classpath:/spring-bean.xml" }); new String[] { "classpath:/spring-bean.xml" });
new WeixinServerBootstrap(aesToken) new WeixinServerBootstrap(aesToken)
.handlerPackagesToScan(handlerPackage).openAlwaysResponse() .handlerPackagesToScan(handlerPackage).openAlwaysResponse()
.resolveBeanFactory(new SpringBeanFactory(applicationContext)) .resolveBeanFactory(new SpringBeanFactory(applicationContext))
.startup(port); .startup(port);
} }
} }

View File

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