bug 120
This commit is contained in:
parent
6464ba8842
commit
4c0f96fcfe
@ -7,6 +7,7 @@
|
|||||||
<artifactId>weixin4j</artifactId>
|
<artifactId>weixin4j</artifactId>
|
||||||
<version>1.7.6</version>
|
<version>1.7.6</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
<packaging>war</packaging>
|
||||||
<artifactId>weixin4j-example</artifactId>
|
<artifactId>weixin4j-example</artifactId>
|
||||||
<version>1.0</version>
|
<version>1.0</version>
|
||||||
<name>weixin4j-example</name>
|
<name>weixin4j-example</name>
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
package com.foxinmy.weixin4j.example.server;
|
package com.foxinmy.weixin4j.example.server;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
@ -19,76 +16,71 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
|
|||||||
* 微信消息服务:需要另外开启一个线程去启动服务,这里值得注意的时:weixin4j-serve本身是作为一个单独的服务来启动的,可以不依赖Spring容器,
|
* 微信消息服务:需要另外开启一个线程去启动服务,这里值得注意的时:weixin4j-serve本身是作为一个单独的服务来启动的,可以不依赖Spring容器,
|
||||||
* 但考虑到目前都是Spring mvc的架构,这里就需要使用一个独立的线程去启动服务,其实本身没有使用spring mvc的API,
|
* 但考虑到目前都是Spring mvc的架构,这里就需要使用一个独立的线程去启动服务,其实本身没有使用spring mvc的API,
|
||||||
* 以后会考虑支持servlet api去集成不同的web框架。
|
* 以后会考虑支持servlet api去集成不同的web框架。
|
||||||
*
|
*
|
||||||
* @className Weixin4jServerStartupWithThread
|
* @className Weixin4jServerStartupWithThread
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月7日
|
* @date 2015年5月7日
|
||||||
* @since JDK 1.6
|
* @since JDK 1.6
|
||||||
*/
|
*/
|
||||||
public class Weixin4jServerStartupWithThread implements ApplicationContextAware {
|
public class Weixin4jServerStartupWithThread implements ApplicationContextAware {
|
||||||
/**
|
/**
|
||||||
* 服务监听的端口号,目前微信只支持80端口,可以考虑用nginx做转发到此端口
|
* 服务监听的端口号,目前微信只支持80端口,可以考虑用nginx做转发到此端口
|
||||||
*/
|
*/
|
||||||
private final int port;
|
private final int port;
|
||||||
/**
|
/**
|
||||||
* 服务器token信息
|
* 服务器token信息
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* 明文模式:String aesToken = ""; 密文模式:AesToken aesToken = new
|
* 明文模式:String aesToken = ""; 密文模式:AesToken aesToken = new
|
||||||
* AesToken("公众号appid", "公众号token","公众号加密/解密消息的密钥");
|
* AesToken("公众号appid", "公众号token","公众号加密/解密消息的密钥");
|
||||||
*/
|
*/
|
||||||
private final AesToken aesToken;
|
private final AesToken aesToken;
|
||||||
/**
|
/**
|
||||||
* 处理微信消息的全限包名(也可通过addHandler方式一个一个添加)
|
* 处理微信消息的全限包名(也可通过addHandler方式一个一个添加)
|
||||||
*/
|
*/
|
||||||
private final String handlerPackage;
|
private final String handlerPackage;
|
||||||
/**
|
/**
|
||||||
* 用spring去获取bean
|
* 用spring去获取bean
|
||||||
*/
|
*/
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
private Weixin4jServerStartupWithThread(int port, AesToken aesToken,
|
private Weixin4jServerStartupWithThread(int port, AesToken aesToken, String handlerPackage) {
|
||||||
String handlerPackage) {
|
this.port = port;
|
||||||
this.port = port;
|
this.aesToken = aesToken;
|
||||||
this.aesToken = aesToken;
|
this.handlerPackage = handlerPackage;
|
||||||
this.handlerPackage = handlerPackage;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setApplicationContext(ApplicationContext applicationContext)
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
throws BeansException {
|
this.applicationContext = applicationContext;
|
||||||
this.applicationContext = applicationContext;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private ExecutorService executor;
|
private WeixinServerBootstrap bootstrap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动函数
|
* 启动函数
|
||||||
*
|
*
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public void start() {
|
public void start() {
|
||||||
executor = Executors.newCachedThreadPool();
|
new Thread(new Runnable() {
|
||||||
executor.execute(new Runnable() {
|
@Override
|
||||||
@Override
|
public void run() {
|
||||||
public void run() {
|
try {
|
||||||
try {
|
bootstrap = new WeixinServerBootstrap(aesToken) // 指定开发者token信息。
|
||||||
new WeixinServerBootstrap(aesToken) // 指定开发者token信息。
|
.handlerPackagesToScan(handlerPackage) // 扫描处理消息的包。
|
||||||
.handlerPackagesToScan(handlerPackage) // 扫描处理消息的包。
|
.resolveBeanFactory(new SpringBeanFactory(applicationContext)) // 声明处理消息类由Spring容器去实例化。
|
||||||
.resolveBeanFactory(
|
.addHandler(DebugMessageHandler.global) // 当没有匹配到消息处理时输出调试信息,开发环境打开。
|
||||||
new SpringBeanFactory(applicationContext)) // 声明处理消息类由Spring容器去实例化。
|
.openAlwaysResponse(); // 当没有匹配到消息处理时输出空白回复(公众号不会出现「该公众号无法提供服务的提示」),正式环境打开。
|
||||||
.addHandler(DebugMessageHandler.global) // 当没有匹配到消息处理时输出调试信息,开发环境打开。
|
bootstrap.startup(port); // 绑定服务的端口号,即对外暴露(微信服务器URL地址)的服务端口。
|
||||||
.openAlwaysResponse() // 当没有匹配到消息处理时输出空白回复(公众号不会出现「该公众号无法提供服务的提示」),正式环境打开。
|
} catch (WeixinException e) {
|
||||||
.startup(port); // 绑定服务的端口号,即对外暴露(微信服务器URL地址)的服务端口。
|
InternalLoggerFactory.getInstance(getClass()).error("weixin4j server startup:FAIL", e);
|
||||||
} catch (WeixinException e) {
|
}
|
||||||
InternalLoggerFactory.getInstance(getClass()).error(
|
}
|
||||||
"weixin4j server startup:FAIL", e);
|
}).start();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
executor.shutdown();
|
bootstrap.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
xsi:schemaLocation="
|
xsi:schemaLocation="
|
||||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
|
||||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
|
||||||
|
|
||||||
<import resource="spring-weixin4j-proxy.xml" />
|
<import resource="spring-weixin4j-proxy.xml" />
|
||||||
<import resource="spring-weixin4j-server.xml" />
|
<import resource="spring-weixin4j-server.xml" />
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
@ -1,14 +1,5 @@
|
|||||||
package com.foxinmy.weixin4j.socket;
|
package com.foxinmy.weixin4j.socket;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
|
||||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@ -22,93 +13,78 @@ import com.foxinmy.weixin4j.util.MessageUtil;
|
|||||||
import com.foxinmy.weixin4j.util.ServerToolkits;
|
import com.foxinmy.weixin4j.util.ServerToolkits;
|
||||||
import com.foxinmy.weixin4j.xml.EncryptMessageHandler;
|
import com.foxinmy.weixin4j.xml.EncryptMessageHandler;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信消息解码类
|
* 微信消息解码类
|
||||||
*
|
*
|
||||||
* @className WeixinMessageDecoder
|
* @className WeixinMessageDecoder
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2014年11月13日
|
* @date 2014年11月13日
|
||||||
* @since JDK 1.6
|
* @since JDK 1.6
|
||||||
* @see <a
|
* @see <a href=
|
||||||
* href="http://mp.weixin.qq.com/wiki/0/61c3a8b9d50ac74f18bdf2e54ddfc4e0.html">加密接入指引</a>
|
* "http://mp.weixin.qq.com/wiki/0/61c3a8b9d50ac74f18bdf2e54ddfc4e0.html">加密接入指引</a>
|
||||||
* @see com.foxinmy.weixin4j.request.WeixinRequest
|
* @see com.foxinmy.weixin4j.request.WeixinRequest
|
||||||
*/
|
*/
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public class WeixinMessageDecoder extends
|
public class WeixinMessageDecoder extends MessageToMessageDecoder<FullHttpRequest> {
|
||||||
MessageToMessageDecoder<FullHttpRequest> {
|
private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
|
||||||
private final InternalLogger logger = InternalLoggerFactory
|
|
||||||
.getInstance(getClass());
|
|
||||||
|
|
||||||
private Map<String, AesToken> aesTokenMap = new ConcurrentHashMap<String, AesToken>();
|
private Map<String, AesToken> aesTokenMap = new ConcurrentHashMap<String, AesToken>();
|
||||||
|
|
||||||
public WeixinMessageDecoder(final Map<String, AesToken> aesTokenMap) {
|
public WeixinMessageDecoder(final Map<String, AesToken> aesTokenMap) {
|
||||||
for (Entry<String, AesToken> entry : aesTokenMap.entrySet()) {
|
for (Entry<String, AesToken> entry : aesTokenMap.entrySet()) {
|
||||||
this.aesTokenMap.put(entry.getKey() == null ? "" : entry.getKey(),
|
this.aesTokenMap.put(entry.getKey() == null ? "" : entry.getKey(), entry.getValue());
|
||||||
entry.getValue());
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public int addAesToken(final AesToken asetoken) {
|
public void addAesToken(final AesToken asetoken) {
|
||||||
AesToken token = aesTokenMap.get(asetoken.getWeixinId());
|
aesTokenMap.put(asetoken.getWeixinId(), asetoken);
|
||||||
if (token != null)
|
}
|
||||||
return -1;
|
|
||||||
aesTokenMap.put(asetoken.getWeixinId(), asetoken);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req,
|
protected void decode(ChannelHandlerContext ctx, FullHttpRequest req, List<Object> out) throws WeixinException {
|
||||||
List<Object> out) throws WeixinException {
|
String messageContent = req.content().toString(ServerToolkits.UTF_8);
|
||||||
String messageContent = req.content().toString(ServerToolkits.UTF_8);
|
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.uri(), true);
|
||||||
QueryStringDecoder queryDecoder = new QueryStringDecoder(req.uri(),
|
HttpMethod method = req.method();
|
||||||
true);
|
logger.info("decode request:{} use {} method invoking", req.uri(), method);
|
||||||
HttpMethod method = req.method();
|
Map<String, List<String>> parameters = queryDecoder.parameters();
|
||||||
logger.info("decode request:{} use {} method invoking", req.uri(),
|
EncryptType encryptType = parameters.containsKey("encrypt_type")
|
||||||
method);
|
? EncryptType.valueOf(parameters.get("encrypt_type").get(0).toUpperCase()) : EncryptType.RAW;
|
||||||
Map<String, List<String>> parameters = queryDecoder.parameters();
|
String echoStr = parameters.containsKey("echostr") ? parameters.get("echostr").get(0) : "";
|
||||||
EncryptType encryptType = parameters.containsKey("encrypt_type") ? EncryptType
|
String timeStamp = parameters.containsKey("timestamp") ? parameters.get("timestamp").get(0) : "";
|
||||||
.valueOf(parameters.get("encrypt_type").get(0).toUpperCase())
|
String nonce = parameters.containsKey("nonce") ? parameters.get("nonce").get(0) : "";
|
||||||
: EncryptType.RAW;
|
String signature = parameters.containsKey("signature") ? parameters.get("signature").get(0) : "";
|
||||||
String echoStr = parameters.containsKey("echostr") ? parameters.get(
|
String msgSignature = parameters.containsKey("msg_signature") ? parameters.get("msg_signature").get(0) : "";
|
||||||
"echostr").get(0) : "";
|
String weixinId = parameters.containsKey("weixin_id") ? parameters.get("weixin_id").get(0) : "";
|
||||||
String timeStamp = parameters.containsKey("timestamp") ? parameters
|
AesToken aesToken = aesTokenMap.get(weixinId);
|
||||||
.get("timestamp").get(0) : "";
|
String encryptContent = null;
|
||||||
String nonce = parameters.containsKey("nonce") ? parameters
|
if (!ServerToolkits.isBlank(messageContent) && encryptType == EncryptType.AES) {
|
||||||
.get("nonce").get(0) : "";
|
if (ServerToolkits.isBlank(aesToken.getAesKey())) {
|
||||||
String signature = parameters.containsKey("signature") ? parameters
|
throw new WeixinException("EncodingAESKey not be empty in safety(AES) mode");
|
||||||
.get("signature").get(0) : "";
|
}
|
||||||
String msgSignature = parameters.containsKey("msg_signature") ? parameters
|
EncryptMessageHandler encryptHandler = EncryptMessageHandler.parser(messageContent);
|
||||||
.get("msg_signature").get(0) : "";
|
encryptContent = encryptHandler.getEncryptContent();
|
||||||
String weixinId = parameters.containsKey("weixin_id") ? parameters.get(
|
/**
|
||||||
"weixin_id").get(0) : "";
|
* 企业号第三方套件 ╮(╯_╰)╭
|
||||||
AesToken aesToken = aesTokenMap.get(weixinId);
|
*/
|
||||||
String encryptContent = null;
|
if (aesToken.getWeixinId().startsWith("tj")) {
|
||||||
if (!ServerToolkits.isBlank(messageContent)
|
aesToken = new AesToken(encryptHandler.getToUserName(), aesToken.getToken(), aesToken.getAesKey());
|
||||||
&& encryptType == EncryptType.AES) {
|
}
|
||||||
if (ServerToolkits.isBlank(aesToken.getAesKey())) {
|
messageContent = MessageUtil.aesDecrypt(aesToken.getWeixinId(), aesToken.getAesKey(), encryptContent);
|
||||||
throw new WeixinException(
|
}
|
||||||
"EncodingAESKey not be empty in safety(AES) mode");
|
logger.info("read original message {}", messageContent);
|
||||||
}
|
WeixinRequest request = new WeixinRequest(req.headers(), method, req.uri(), encryptType, echoStr, timeStamp,
|
||||||
EncryptMessageHandler encryptHandler = EncryptMessageHandler
|
nonce, signature, msgSignature, messageContent, encryptContent, aesToken);
|
||||||
.parser(messageContent);
|
request.setDecoderResult(req.decoderResult());
|
||||||
encryptContent = encryptHandler.getEncryptContent();
|
request.setProtocolVersion(req.protocolVersion());
|
||||||
/**
|
out.add(request);
|
||||||
* 企业号第三方套件 ╮(╯_╰)╭
|
}
|
||||||
*/
|
|
||||||
if (aesToken.getWeixinId().startsWith("tj")) {
|
|
||||||
aesToken = new AesToken(encryptHandler.getToUserName(),
|
|
||||||
aesToken.getToken(), aesToken.getAesKey());
|
|
||||||
}
|
|
||||||
messageContent = MessageUtil.aesDecrypt(aesToken.getWeixinId(),
|
|
||||||
aesToken.getAesKey(), encryptContent);
|
|
||||||
}
|
|
||||||
logger.info("read original message {}", messageContent);
|
|
||||||
WeixinRequest request = new WeixinRequest(req.headers(), method,
|
|
||||||
req.uri(), encryptType, echoStr, timeStamp, nonce,
|
|
||||||
signature, msgSignature, messageContent, encryptContent,
|
|
||||||
aesToken);
|
|
||||||
request.setDecoderResult(req.decoderResult());
|
|
||||||
request.setProtocolVersion(req.protocolVersion());
|
|
||||||
out.add(request);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
package com.foxinmy.weixin4j.socket;
|
package com.foxinmy.weixin4j.socket;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
|
||||||
|
import com.foxinmy.weixin4j.util.AesToken;
|
||||||
|
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
import io.netty.handler.codec.http.HttpServerCodec;
|
import io.netty.handler.codec.http.HttpServerCodec;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.foxinmy.weixin4j.dispatcher.WeixinMessageDispatcher;
|
|
||||||
import com.foxinmy.weixin4j.util.AesToken;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信消息服务器初始化
|
* 微信消息服务器初始化
|
||||||
*
|
*
|
||||||
* @className WeixinServerInitializer
|
* @className WeixinServerInitializer
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2015年5月17日
|
* @date 2015年5月17日
|
||||||
@ -22,27 +22,26 @@ import com.foxinmy.weixin4j.util.AesToken;
|
|||||||
*/
|
*/
|
||||||
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
|
public class WeixinServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||||
|
|
||||||
private final WeixinMessageDispatcher messageDispatcher;
|
private final WeixinMessageDispatcher messageDispatcher;
|
||||||
private final WeixinMessageDecoder messageDecoder;
|
private final WeixinMessageDecoder messageDecoder;
|
||||||
|
|
||||||
public WeixinServerInitializer(Map<String, AesToken> aesTokenMap,
|
public WeixinServerInitializer(Map<String, AesToken> aesTokenMap, WeixinMessageDispatcher messageDispatcher) {
|
||||||
WeixinMessageDispatcher messageDispatcher) {
|
this.messageDispatcher = messageDispatcher;
|
||||||
this.messageDispatcher = messageDispatcher;
|
this.messageDecoder = new WeixinMessageDecoder(aesTokenMap);
|
||||||
this.messageDecoder = new WeixinMessageDecoder(aesTokenMap);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public int addAesToken(final AesToken asetoken) {
|
public void addAesToken(AesToken asetoken) {
|
||||||
return messageDecoder.addAesToken(asetoken);
|
messageDecoder.addAesToken(asetoken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(SocketChannel channel) {
|
protected void initChannel(SocketChannel channel) {
|
||||||
ChannelPipeline pipeline = channel.pipeline();
|
ChannelPipeline pipeline = channel.pipeline();
|
||||||
pipeline.addLast(new HttpServerCodec());
|
pipeline.addLast(new HttpServerCodec());
|
||||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||||
pipeline.addLast(messageDecoder);
|
pipeline.addLast(messageDecoder);
|
||||||
pipeline.addLast(new WeixinResponseEncoder());
|
pipeline.addLast(new WeixinResponseEncoder());
|
||||||
pipeline.addLast(new SingleResponseEncoder());
|
pipeline.addLast(new SingleResponseEncoder());
|
||||||
pipeline.addLast(new WeixinRequestHandler(messageDispatcher));
|
pipeline.addLast(new WeixinRequestHandler(messageDispatcher));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,5 @@
|
|||||||
package com.foxinmy.weixin4j.startup;
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -30,9 +18,21 @@ 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;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.bootstrap.ServerBootstrapConfig;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelOption;
|
||||||
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信netty服务启动程序
|
* 微信netty服务启动程序
|
||||||
*
|
*
|
||||||
* @className WeixinServerBootstrap
|
* @className WeixinServerBootstrap
|
||||||
* @author jinyu(foxinmy@gmail.com)
|
* @author jinyu(foxinmy@gmail.com)
|
||||||
* @date 2014年10月12日
|
* @date 2014年10月12日
|
||||||
@ -45,309 +45,308 @@ import com.foxinmy.weixin4j.util.AesToken;
|
|||||||
*/
|
*/
|
||||||
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 * 2
|
* worker线程数,默认设置为DEFAULT_BOSSTHREADS * 2
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
|
||||||
private WeixinServerInitializer wechatInitializer;
|
private ServerBootstrap bootstrap;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
DEFAULT_BOSSTHREADS = Runtime.getRuntime().availableProcessors();
|
DEFAULT_BOSSTHREADS = Runtime.getRuntime().availableProcessors();
|
||||||
DEFAULT_WORKERTHREADS = DEFAULT_BOSSTHREADS * 2;
|
DEFAULT_WORKERTHREADS = DEFAULT_BOSSTHREADS * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* 明文模式
|
* 明文模式
|
||||||
*
|
*
|
||||||
* @param token
|
* @param token
|
||||||
* 开发者token
|
* 开发者token
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public WeixinServerBootstrap(String token) {
|
public WeixinServerBootstrap(String token) {
|
||||||
this("", token, null);
|
this("", token, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 明文模式 & 兼容模式 & 密文模式
|
* 明文模式 & 兼容模式 & 密文模式
|
||||||
* <dl>
|
* <dl>
|
||||||
* <font
|
* <font color=
|
||||||
* color="red">值得注意的是:企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数</font>
|
* "red">值得注意的是:企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数</font>
|
||||||
* </dl>
|
* </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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多个公众号的支持 <dt>值得注意的是:
|
* 多个公众号的支持
|
||||||
* <dl>
|
* <dt>值得注意的是:
|
||||||
* <font color="red">1).企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数</font>
|
* <dl>
|
||||||
* </dl>
|
* <font color="red">1).企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数</font>
|
||||||
* <dl>
|
* </dl>
|
||||||
* <font
|
* <dl>
|
||||||
* color="red">2).非明文模式下需要在服务器URL后面加多一个`weixin_id=对应的appid/corpid`的参数</font>
|
* <font color=
|
||||||
* </dl>
|
* "red">2).非明文模式下需要在服务器URL后面加多一个`weixin_id=对应的appid/corpid`的参数</font>
|
||||||
*
|
* </dl>
|
||||||
* @param aesTokens
|
*
|
||||||
* 多个公众号
|
* @param aesTokens
|
||||||
* @return
|
* 多个公众号
|
||||||
*/
|
* @return
|
||||||
public WeixinServerBootstrap(AesToken... aesToken) {
|
*/
|
||||||
this(new DefaultMessageMatcher(), aesToken);
|
public WeixinServerBootstrap(AesToken... aesToken) {
|
||||||
}
|
this(new DefaultMessageMatcher(), aesToken);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多个公众号的支持 <dt>值得注意的是:
|
* 多个公众号的支持
|
||||||
* <dl>
|
* <dt>值得注意的是:
|
||||||
* <font color="red">1).企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数</font>
|
* <dl>
|
||||||
* </dl>
|
* <font color="red">1).企业号服务时需要在服务器URL后面加多一个`encrypt_type=aes`的参数</font>
|
||||||
* <dl>
|
* </dl>
|
||||||
* <font
|
* <dl>
|
||||||
* color="red">2).非明文模式下需要在服务器URL后面加多一个`weixin_id=对应的appid/corpid`的参数</font>
|
* <font color=
|
||||||
* </dl>
|
* "red">2).非明文模式下需要在服务器URL后面加多一个`weixin_id=对应的appid/corpid`的参数</font>
|
||||||
*
|
* </dl>
|
||||||
* @param messageMatcher
|
*
|
||||||
* 消息匹配器
|
* @param messageMatcher
|
||||||
* @param aesTokens
|
* 消息匹配器
|
||||||
* 公众号信息
|
* @param aesTokens
|
||||||
* @return
|
* 公众号信息
|
||||||
*/
|
* @return
|
||||||
public WeixinServerBootstrap(WeixinMessageMatcher messageMatcher,
|
*/
|
||||||
AesToken... aesTokens) {
|
public WeixinServerBootstrap(WeixinMessageMatcher messageMatcher, AesToken... aesTokens) {
|
||||||
if (messageMatcher == null) {
|
if (messageMatcher == null) {
|
||||||
throw new IllegalArgumentException("MessageMatcher not be null");
|
throw new IllegalArgumentException("MessageMatcher not be null");
|
||||||
}
|
}
|
||||||
if (aesTokens == null) {
|
if (aesTokens == null) {
|
||||||
throw new IllegalArgumentException("AesToken not be null");
|
throw new IllegalArgumentException("AesToken not be null");
|
||||||
}
|
}
|
||||||
this.aesTokenMap = new HashMap<String, AesToken>();
|
this.aesTokenMap = new HashMap<String, AesToken>();
|
||||||
for (AesToken aesToken : aesTokens) {
|
for (AesToken aesToken : aesTokens) {
|
||||||
this.aesTokenMap.put(aesToken.getWeixinId(), aesToken);
|
this.aesTokenMap.put(aesToken.getWeixinId(), aesToken);
|
||||||
}
|
}
|
||||||
this.aesTokenMap.put("", aesTokens[0]);
|
this.aesTokenMap.put("", aesTokens[0]);
|
||||||
this.messageHandlerList = new ArrayList<WeixinMessageHandler>();
|
this.messageHandlerList = new ArrayList<WeixinMessageHandler>();
|
||||||
this.messageInterceptorList = new ArrayList<WeixinMessageInterceptor>();
|
this.messageInterceptorList = new ArrayList<WeixinMessageInterceptor>();
|
||||||
this.messageDispatcher = new WeixinMessageDispatcher(messageMatcher);
|
this.messageDispatcher = new WeixinMessageDispatcher(messageMatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认端口(30000)启动服务
|
* 默认端口(30000)启动服务
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void startup() throws WeixinException {
|
public void startup() throws WeixinException {
|
||||||
startup(DEFAULT_SERVERPORT);
|
startup(DEFAULT_SERVERPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 指定端口启动服务
|
* 指定端口启动服务
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void startup(int serverPort) throws WeixinException {
|
public void startup(int serverPort) throws WeixinException {
|
||||||
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort);
|
startup(DEFAULT_BOSSTHREADS, DEFAULT_WORKERTHREADS, serverPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 接受参数启动服务
|
* 接受参数启动服务
|
||||||
*
|
*
|
||||||
* @param bossThreads
|
* @param bossThreads
|
||||||
* boss线程数
|
* boss线程数
|
||||||
* @param workerThreads
|
* @param workerThreads
|
||||||
* worker线程数
|
* worker线程数
|
||||||
* @param serverPort
|
* @param serverPort
|
||||||
* 服务启动端口
|
* 服务启动端口
|
||||||
* @return
|
* @return
|
||||||
* @throws WeixinException
|
* @throws WeixinException
|
||||||
*/
|
*/
|
||||||
public void startup(int bossThreads, int workerThreads, final int serverPort)
|
public void startup(int bossThreads, int workerThreads, final int serverPort) throws WeixinException {
|
||||||
throws WeixinException {
|
messageDispatcher.setMessageHandlerList(messageHandlerList);
|
||||||
messageDispatcher.setMessageHandlerList(messageHandlerList);
|
messageDispatcher.setMessageInterceptorList(messageInterceptorList);
|
||||||
messageDispatcher.setMessageInterceptorList(messageInterceptorList);
|
try {
|
||||||
|
bootstrap = new ServerBootstrap();
|
||||||
|
bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
|
||||||
|
bootstrap.group(new NioEventLoopGroup(bossThreads), new NioEventLoopGroup(workerThreads))
|
||||||
|
.channel(NioServerSocketChannel.class).handler(new LoggingHandler())
|
||||||
|
.childHandler(new WeixinServerInitializer(aesTokenMap, messageDispatcher));
|
||||||
|
Channel ch = bootstrap.bind(serverPort).addListener(new FutureListener<Void>() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(Future<Void> 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 {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EventLoopGroup bossGroup = new NioEventLoopGroup(bossThreads);
|
public boolean shutdown() {
|
||||||
EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
|
if (bootstrap == null) {
|
||||||
try {
|
return false;
|
||||||
wechatInitializer = new WeixinServerInitializer(aesTokenMap,
|
}
|
||||||
messageDispatcher);
|
ServerBootstrapConfig c = bootstrap.config();
|
||||||
ServerBootstrap b = new ServerBootstrap();
|
c.group().shutdownGracefully();
|
||||||
b.option(ChannelOption.SO_BACKLOG, 1024);
|
c.childGroup().shutdownGracefully();
|
||||||
b.group(bossGroup, workerGroup)
|
messageHandlerList = null;
|
||||||
.channel(NioServerSocketChannel.class)
|
messageInterceptorList = null;
|
||||||
.handler(new LoggingHandler())
|
messageDispatcher = null;
|
||||||
.childHandler(wechatInitializer);
|
bootstrap = null;
|
||||||
Channel ch = b.bind(serverPort)
|
return true;
|
||||||
.addListener(new FutureListener<Void>() {
|
}
|
||||||
@Override
|
|
||||||
public void operationComplete(Future<Void> 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
|
* @param messageHandler
|
||||||
* 消息处理器
|
* 消息处理器
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public WeixinServerBootstrap addHandler(
|
public WeixinServerBootstrap addHandler(WeixinMessageHandler... messageHandler) {
|
||||||
WeixinMessageHandler... messageHandler) {
|
if (messageHandler == null) {
|
||||||
if (messageHandler == null) {
|
throw new IllegalArgumentException("messageHandler not be null");
|
||||||
throw new IllegalArgumentException("messageHandler not be null");
|
}
|
||||||
}
|
messageHandlerList.addAll(Arrays.asList(messageHandler));
|
||||||
messageHandlerList.addAll(Arrays.asList(messageHandler));
|
return this;
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插入一个或多个消息拦截器
|
* 插入一个或多个消息拦截器
|
||||||
*
|
*
|
||||||
* @param messageInterceptor
|
* @param messageInterceptor
|
||||||
* 消息拦截器
|
* 消息拦截器
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public WeixinServerBootstrap addInterceptor(
|
public WeixinServerBootstrap addInterceptor(WeixinMessageInterceptor... messageInterceptor) {
|
||||||
WeixinMessageInterceptor... messageInterceptor) {
|
if (messageInterceptor == null) {
|
||||||
if (messageInterceptor == null) {
|
throw new IllegalArgumentException("messageInterceptor not be null");
|
||||||
throw new IllegalArgumentException("messageInterceptor not be null");
|
}
|
||||||
}
|
messageInterceptorList.addAll(Arrays.asList(messageInterceptor));
|
||||||
messageInterceptorList.addAll(Arrays.asList(messageInterceptor));
|
return this;
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按照包名去添加消息处理器
|
* 按照包名去添加消息处理器
|
||||||
*
|
*
|
||||||
* @param messageHandlerPackages
|
* @param messageHandlerPackages
|
||||||
* 消息处理器所在的包名
|
* 消息处理器所在的包名
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public WeixinServerBootstrap handlerPackagesToScan(
|
public WeixinServerBootstrap handlerPackagesToScan(String... messageHandlerPackages) {
|
||||||
String... messageHandlerPackages) {
|
if (messageHandlerPackages == null) {
|
||||||
if (messageHandlerPackages == null) {
|
throw new IllegalArgumentException("messageHandlerPackages not be null");
|
||||||
throw new IllegalArgumentException(
|
}
|
||||||
"messageHandlerPackages not be null");
|
messageDispatcher.setMessageHandlerPackages(messageHandlerPackages);
|
||||||
}
|
return this;
|
||||||
messageDispatcher.setMessageHandlerPackages(messageHandlerPackages);
|
}
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按照包名去添加消息拦截器
|
* 按照包名去添加消息拦截器
|
||||||
*
|
*
|
||||||
* @param messageInterceptorPackages
|
* @param messageInterceptorPackages
|
||||||
* 消息拦截器所在的包名
|
* 消息拦截器所在的包名
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public WeixinServerBootstrap interceptorPackagesToScan(
|
public WeixinServerBootstrap interceptorPackagesToScan(String... messageInterceptorPackages) {
|
||||||
String... messageInterceptorPackages) {
|
if (messageInterceptorPackages == null) {
|
||||||
if (messageInterceptorPackages == null) {
|
throw new IllegalArgumentException("messageInterceptorPackages not be null");
|
||||||
throw new IllegalArgumentException(
|
}
|
||||||
"messageInterceptorPackages not be null");
|
messageDispatcher.setMessageInterceptorPackages(messageInterceptorPackages);
|
||||||
}
|
return this;
|
||||||
messageDispatcher
|
}
|
||||||
.setMessageInterceptorPackages(messageInterceptorPackages);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 声明处理器跟拦截器类实例化的构造工厂,否则通过Class.newInstance的方式构造
|
* 声明处理器跟拦截器类实例化的构造工厂,否则通过Class.newInstance的方式构造
|
||||||
*
|
*
|
||||||
* @param beanFactory
|
* @param beanFactory
|
||||||
* Bean构造工厂
|
* Bean构造工厂
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public WeixinServerBootstrap resolveBeanFactory(BeanFactory beanFactory) {
|
public WeixinServerBootstrap resolveBeanFactory(BeanFactory beanFactory) {
|
||||||
messageDispatcher.setBeanFactory(beanFactory);
|
messageDispatcher.setBeanFactory(beanFactory);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册消息类型
|
* 注册消息类型
|
||||||
*
|
*
|
||||||
* @param messageKey
|
* @param messageKey
|
||||||
* 消息key
|
* 消息key
|
||||||
* @param messageClass
|
* @param messageClass
|
||||||
* 消息类
|
* 消息类
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public WeixinServerBootstrap registMessageClass(
|
public WeixinServerBootstrap registMessageClass(WeixinMessageKey messageKey,
|
||||||
WeixinMessageKey messageKey,
|
Class<? extends WeixinMessage> messageClass) {
|
||||||
Class<? extends WeixinMessage> messageClass) {
|
messageDispatcher.registMessageClass(messageKey, messageClass);
|
||||||
messageDispatcher.registMessageClass(messageKey, messageClass);
|
return this;
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开总是响应开关,如未匹配到MessageHandler时回复空白消息
|
* 打开总是响应开关,如未匹配到MessageHandler时回复空白消息
|
||||||
*/
|
*/
|
||||||
public WeixinServerBootstrap openAlwaysResponse() {
|
public WeixinServerBootstrap openAlwaysResponse() {
|
||||||
messageDispatcher.openAlwaysResponse();
|
messageDispatcher.openAlwaysResponse();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* aesTokenMap 最好是线程安全的
|
* 动态添加aesToken
|
||||||
*
|
*
|
||||||
* @param aesToken
|
* @param aesToken
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public int addAesToken(AesToken aesToken) {
|
public boolean addAesToken(AesToken aesToken) {
|
||||||
return wechatInitializer.addAesToken(aesToken);
|
if (bootstrap == null) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
ServerBootstrapConfig c = bootstrap.config();
|
||||||
|
((WeixinServerInitializer) c.childHandler()).addAesToken(aesToken);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public final static String VERSION = "1.1.8";
|
public final static String VERSION = "1.1.8";
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user