此项目将原生socket服务端与netty-socket服务端整合在一起,ssl加密与不加密的socket可以实现配置切换,可根据需求增减模块。
客户端与服务端报文采用以下格式
1字节的特殊标识68H(01101000B)作为报文开头flag + 4字节的数据长度字符串 + 6字节的业务编码字符串(根据它执行不同的业务) + 变长的数据域 + 1字节的特殊标识16H(00010110B)作为报文结束flag
{head:{tag1:value1,tag2:value2,...},body:{tag3:value3,tag4:value4, params:[{ param1:values1 },{ paramn:valuesn }],...}}
head存放协议信息,body存放业务信息。
项目 原生socket与netty共存,即application.main中同时启动2个socket服务
package com.lance;
import com.lance.api.business.socketserver.nativesocket.ServerSocket;
import com.lance.api.business.socketserver.netty.NettyServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import java.util.concurrent.Executor;
/**
* <p>启动类
*
* @author lance
* @since 2018-10-15
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application implements CommandLineRunner
{
@Autowired
private NettyServer nettyServer;
@Autowired
private ServerSocket serverSocket;
@Autowired
private Executor threadAsyncServiceProvider;
public static void main(String[] args)
{
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) throws Exception
{
serverSocket.init();
// 启动新线程是为了原生socket与netty共存启动
threadAsyncServiceProvider.execute(serverSocket);
// 启动netty-socket
nettyServer.start();
}
}
原生ssl socket服务端如下:
package com.lance.api.business.socketserver.nativesocket;
import com.lance.api.business.util.DealSocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.TrustManagerFactory;
import java.net.Socket;
import java.security.KeyStore;
/**
* ssl socket
*
* @author lance
* @since 2018-10-10
*/
@Component("serverSocket")
@Slf4j
public class ServerSocket implements Runnable
{
@Value("${sslSocket.port}")
private int port;
@Value("${sslSocket.server_key_store_password}")
private String serverKeyStorePassword;
@Value("${sslSocket.server_trust_key_store_password}")
private String serverTrustKeyStorePassword;
@Autowired
private DealSocket dealSocket;
private SSLServerSocket serverSocket;
/**
* 启动程序
*/
@Override
public void run()
{
if (serverSocket == null)
{
log.error("ERROR:启动socket服务失败");
return;
}
while (true)
{
try
{
Socket socket = serverSocket.accept();
dealSocket.analysisAndDealMsg(socket);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
/**
* 初始化
*/
public void init()
{
try
{
SSLContext ctx = SSLContext.getInstance("SSL");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
KeyStore ks = KeyStore.getInstance("JKS");
KeyStore tks = KeyStore.getInstance("JKS");
Resource fileResourceK = new ClassPathResource("keystores/kserver.keystore");
Resource fileResourceT = new ClassPathResource("keystores/tserver.keystore");
ks.load(fileResourceK.getInputStream(), serverKeyStorePassword.toCharArray());
tks.load(fileResourceT.getInputStream(), serverTrustKeyStorePassword.toCharArray());
kmf.init(ks, serverKeyStorePassword.toCharArray());
tmf.init(tks);
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(port);
serverSocket.setNeedClientAuth(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
ComposeUtil与DealSocket下面做介绍,暂时略过
netty服务端如下:
package com.lance.api.business.socketserver.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManagerFactory;
import java.security.KeyStore;
/**
* netty服务启动类
*
* @author lance
*/
@Component
@Slf4j
public class NettyServer extends ChannelInitializer
{
@Value("${sslSocket.nettyPort}")
private int port;
@Value("${sslSocket.sslFlag:false}")
private boolean ssl;
@Value("${sslSocket.server_key_store_password}")
private String serverKeyStorePassword;
@Value("${sslSocket.server_trust_key_store_password}")
private String serverTrustKeyStorePassWord;
private static final EventLoopGroup BOSS_GROUP = new NioEventLoopGroup();
private static final EventLoopGroup WORKER_GROUP = new NioEventLoopGroup();
/**
* 业务处理器
*/
@Autowired
private ServerHandler serverHandler;
/**
* 关闭服务
*/
@PreDestroy
public void close()
{
log.info("关闭服务....");
BOSS_GROUP.shutdownGracefully();
WORKER_GROUP.shutdownGracefully();
}
/**
* 开启服务
*/
public void start() throws Exception
{
final SSLContext sslCtx;
if (ssl)
{
sslCtx = SSLContext.getInstance("SSL");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
KeyStore ks = KeyStore.getInstance("JKS");
KeyStore tks = KeyStore.getInstance("JKS");
org.springframework.core.io.Resource frk = new ClassPathResource("keystores/kserver.keystore");
org.springframework.core.io.Resource frt = new ClassPathResource("keystores/tserver.keystore");
ks.load(frk.getInputStream(), serverKeyStorePassword.toCharArray());
tks.load(frt.getInputStream(), serverTrustKeyStorePassWord.toCharArray());
kmf.init(ks, serverKeyStorePassword.toCharArray());
tmf.init(tks);
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
}
else
{
sslCtx = null;
}
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(BOSS_GROUP, WORKER_GROUP);
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.option(ChannelOption.SO_BACKLOG, 100);
try
{
//设置事件处理
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>()
{
@Override
protected void initChannel(SocketChannel ch) throws Exception
{
if (sslCtx != null)
{
SSLEngine sslEngine = sslCtx.createSSLEngine();
sslEngine.setUseClientMode(false);
ch.pipeline().addFirst(new SslHandler(sslEngine));
}
ChannelPipeline pipeline = ch.pipeline();
// 解码器
pipeline.addLast(new MessageDecoder());
// 编码器
pipeline.addLast(new MessageEncoder());
// 业务处理器
pipeline.addLast(serverHandler);
}
});
log.info("netty服务在[{}]端口启动监听", port);
ChannelFuture future = serverBootstrap.bind(port).sync();
future.channel().closeFuture().sync();
}
catch (InterruptedException e)
{
log.info("[出现异常] 释放资源");
BOSS_GROUP.shutdownGracefully();
WORKER_GROUP.shutdownGracefully();
}
}
@Override
protected void initChannel(Channel channel) throws Exception
{
}
}
netty需要三个适配器:业务处理前解码 messageDecoder;业务处理 serverHandler;业务处理后编码 messageEncoder
解码器是将传入的字节数组转换为javaBean,代码如下:
package com.lance.api.business.socketserver.netty;
import com.lance.api.business.pojo.model.EntryModel;
import com.lance.api.business.util.ByteDataBuffer;
import com.lance.api.business.util.ComposeUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 报文解码
*
* @author lance
*/
public class MessageDecoder extends ByteToMessageDecoder
{
/**
* 从ByteBuf中获取字节,转换成对象
*
* @param ctx
* @param buffer
* @param out
* @throws Exception
*/
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception
{
int len = buffer.readableBytes();
byte[] req = new byte[len];
buffer.getBytes(0, req);
ByteDataBuffer dataBuffer = new ByteDataBuffer(req);
EntryModel entryModel = ComposeUtil.deSplit(dataBuffer);
out.add(entryModel);
buffer.skipBytes(len);
}
}
编码器是将对象转为固定格式的字节数组,传输给客户端,代码如下:
package com.lance.api.business.socketserver.netty;
import com.lance.api.business.pojo.model.ReturnModel;
import com.lance.api.business.util.ComposeUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.springframework.stereotype.Component;
/**
* 报文组装
*
* @author lance
*/
public class MessageEncoder extends MessageToByteEncoder<ReturnModel>
{
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, ReturnModel returnModel, ByteBuf out) throws Exception
{
byte[] bytes = null;
try
{
bytes = ComposeUtil.doCanProcess(returnModel.getMap(), returnModel.getServCode(), returnModel.getMsgId());
}
catch (Exception e)
{
e.printStackTrace();
}
out.writeBytes(bytes);
}
}
业务处理适配器:
package com.lance.api.business.socketserver.netty;
// 记录调用方法的元信息的类
import com.lance.api.business.pojo.model.EntryModel;
import com.lance.api.business.pojo.model.ReturnModel;
import com.lance.api.business.util.DealSocket;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* netty业务处理类
*
* @author lance
*/
@Component
@Sharable
@Slf4j
public class ServerHandler extends ChannelInboundHandlerAdapter
{
@Autowired
private DealSocket dealSocket;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
{
EntryModel entryModel = (EntryModel) msg;
String servCode = entryModel.getHeadModel().getServCode();
String msgId = entryModel.getHeadModel().getMsgId();
// do business
MDC.put("logId", msgId);
ReturnModel result = dealSocket.doBusiness(servCode, msgId, entryModel);
MDC.clear();
ctx.writeAndFlush(result);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception
{
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
{
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
ComposeUtil为自定义的解析、构建 先前描述的格式报文的工具类
package com.lance.api.business.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.lance.api.business.pojo.model.EntryBodyModel;
import com.lance.api.business.pojo.model.EntryHeadModel;
import com.lance.api.business.pojo.model.EntryModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* <p>报文组装解析类
*
* @author lance
* @since 2018-10-11
*/
@Slf4j
@Component
public class ComposeUtil
{
private static final byte BLOCK_BGN_SIGN = 0x68;
private static final byte BLOCK_END_SIGN = 0x16;
private static final String ENCODING = "UTF-8";
/**
* 组装报文
*
* @param paraMap
* @param serverCode
* @param msgId
* @return
* @throws Exception
*/
public static byte[] doCanProcess(Map paraMap, String serverCode, String msgId) throws Exception
{
Map<String, Object> map = new HashMap<>();
Map<String, String> headMap = new HashMap<>();
// 服务编码
String servCode = serverCode;
headMap.put("version", "1.0.0");
// 服务编码
headMap.put("servCode", servCode);
// msgId 32位不可重复的流水
headMap.put("msgId", msgId);
headMap.put("msgTime", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));
headMap.put("extend", "");
map.put("head", headMap);
map.put("body", paraMap);
String message = JSONObject.toJSONString(map).replace("\\", "");
log.info("返回的报文为:\n{}", message);
ByteDataBuffer bdb = new ByteDataBuffer();
bdb.setInBigEndian(false);
// 开始字节
bdb.writeInt8((byte) BLOCK_BGN_SIGN);
int len = Integer.parseInt(getLen(message, 4));
// 用于计算数据域的长度是否大于4字节
// 报文长度
bdb.writeInt32(len);
// 服务编码
bdb.writeString(servCode, 6);
// 报文frame
bdb.writeBytes(message.getBytes());
// 结束字节
bdb.writeInt8((byte) BLOCK_END_SIGN);
return bdb.getBytes();
}
/**
* 解析报文
*
* @param obj
* @return
* @throws Exception
*/
public static EntryModel deSplit(ByteDataBuffer obj) throws Exception
{
ByteDataBuffer dataBuf = obj;
dataBuf.setEncoding(ENCODING);
dataBuf.setInBigEndian(false);
// 长度
int totalLen = 0;
byte sign = dataBuf.readInt8();
if (sign != BLOCK_BGN_SIGN)
{
log.info("无法找到起始标记!");
return null;
}
totalLen = dataBuf.readInt32();
dataBuf.readString(6);
byte[] dataBytes = new byte[totalLen];
dataBuf.readBytes(dataBytes);
String message = new String(dataBytes);
log.info("请求的报文为:\n{}", message);
// 报文是json格式,把json报文转换成对象类型的
EntryModel entryModel = JSON.parseObject(message, EntryModel.class);
EntryHeadModel headModel = JSON.parseObject(entryModel.getHead(), new TypeReference<EntryHeadModel>()
{
});
EntryBodyModel bodyModel = JSON.parseObject(entryModel.getBody(), new TypeReference<EntryBodyModel>()
{
});
entryModel.setHeadModel(headModel);
entryModel.setBodyModel(bodyModel);
return entryModel;
}
/**
* 计算报文长度,不足四位左补零
*
* @param text 报文信息
* @param needLen 报文长度规定的字符数
* @return
*/
private static String getLen(String text, int needLen)
{
if (text != null)
{
int len;
try
{
len = text.getBytes("utf-8").length;
String lenStr = String.valueOf(len);
StringBuffer sb = new StringBuffer(lenStr);
while (sb.length() < needLen)
{
sb.insert(0, "0");
}
return sb.toString();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
return null;
}
}
DealSocket为解析传入的6位业务编码,找到对应的业务包装类,进行业务处理
package com.lance.api.business.util;
import com.lance.api.business.pojo.model.EntryModel;
import com.lance.api.business.pojo.model.ReturnModel;
import com.lance.api.business.service.wrap.core.BaseServiceWrapper;
import com.lance.api.business.service.wrap.core.ServiceWrapperContainer;
import com.lance.api.common.base.model.Communication;
import com.lance.api.common.constant.ResponseCode;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.List;
import java.util.Map;
/**
* <p>socket业务分发类
*
* @author lance
* @since 2018-10-15
*/
@Data
@Slf4j
@Component
@SuppressWarnings("unchecked")
public class DealSocket
{
/**
* service包装类容器
*/
@Autowired
private ServiceWrapperContainer serviceWrapperContainer;
/**
* 解析并处理报文
*
* @param socket
* @throws Exception
*/
@Async
public void analysisAndDealMsg(Socket socket) throws Exception
{
/*
1.获取socket数据
*/
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(input);
BufferedOutputStream bos = new BufferedOutputStream(output);
byte[] buffer = new byte[36000];
bis.read(buffer);
// 解析报文
EntryModel entryModel = ComposeUtil.deSplit(new ByteDataBuffer(buffer));
if (entryModel == null)
{
noSignFlag(bos);
return;
}
// 业务请求码
String servCode = entryModel.getHeadModel().getServCode();
// 报文标识
String msgId = entryModel.getHeadModel().getMsgId();
/*
2.业务处理
*/
MDC.put("logId", msgId);
doBusiness(servCode, msgId, entryModel, bos);
MDC.clear();
}
/**
* 处理业务(原生socket使用)
*
* @param servCode
* @param msgId
* @param entryModel
* @param bos
*/
@Async
public void doBusiness(String servCode, String msgId, EntryModel entryModel, BufferedOutputStream bos)
{
Map<String, Object> resultMap = null;
try
{
if (StringUtils.isEmpty(servCode) || StringUtils.isEmpty(msgId))
{
resultMap = Communication.getInstance().getFailedMap(ResponseCode._0000.getKey(), ResponseCode._0000.getValue());
}
else
{
log.info("业务处理开始");
// 根据servCode查询所对应的service业务处理类
BaseServiceWrapper baseServiceWrapper = serviceWrapperContainer.getService(servCode);
// 或有业务存在servCode相同的情况,需加以判断是否存在servCode相同的情况
if (baseServiceWrapper.hasSameService())
{
// 根据servCode查询出所有servCode相同的service
List<BaseServiceWrapper> baseServiceWrappers = serviceWrapperContainer.findSameService(servCode);
for (BaseServiceWrapper bs : baseServiceWrappers)
{
if (bs.sameIsMyDo(entryModel))
{
resultMap = bs.handle(entryModel);
}
}
}
else
{
resultMap = baseServiceWrapper.handle(entryModel);
}
log.info("业务处理完毕");
}
}
catch (Exception e)
{
log.error("业务处理发生异常:\n{}", e.getMessage());
e.printStackTrace();
resultMap = Communication.getInstance().getFailedMap(ResponseCode._0009.getKey(), ResponseCode._0009.getValue());
}
finally
{
try
{
bos.write(ComposeUtil.doCanProcess(resultMap, servCode, msgId));
bos.flush();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
/**
* 处理业务(netty使用)
*
* @param servCode
* @param msgId
* @param entryModel
*/
public ReturnModel doBusiness(String servCode, String msgId, EntryModel entryModel)
{
ReturnModel resultModel = new ReturnModel();
resultModel.setServCode(servCode);
resultModel.setMsgId(msgId);
try
{
if (StringUtils.isEmpty(servCode) || StringUtils.isEmpty(msgId))
{
return resultModel.setMap(Communication.getInstance().getFailedMap(ResponseCode._0000.getKey(), ResponseCode._0000.getValue()));
}
log.info("业务处理开始");
// 根据servCode查询所对应的service业务处理类
BaseServiceWrapper baseServiceWrapper = serviceWrapperContainer.getService(servCode);
// 或有业务存在servCode相同的情况,需加以判断是否存在servCode相同的情况
if (baseServiceWrapper.hasSameService())
{
// 根据servCode查询出所有servCode相同的service
List<BaseServiceWrapper> baseServiceWrappers = serviceWrapperContainer.findSameService(servCode);
for (BaseServiceWrapper bs : baseServiceWrappers)
{
if (bs.sameIsMyDo(entryModel))
{
resultModel.setMap(bs.handle(entryModel));
}
}
}
else
{
resultModel.setMap(baseServiceWrapper.handle(entryModel));
}
log.info("业务处理完毕");
}
catch (Exception e)
{
log.error("业务处理发生异常:\n{}", e.getMessage());
e.printStackTrace();
resultModel.setMap(Communication.getInstance().getFailedMap(ResponseCode._0009.getKey(), ResponseCode._0009.getValue()));
}
return resultModel;
}
/**
* 若无起始标记,则返回无标识信息
*
* @param bos
*/
public void noSignFlag(BufferedOutputStream bos)
{
Map<String, Object> resultMap = Communication.getInstance().getFailedMap(ResponseCode._0000.getKey(), ResponseCode._0000.getValue());
try
{
bos.write(ComposeUtil.doCanProcess(resultMap, "", ""));
bos.flush();
bos.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
怎么实现根据业务编码找到对应的业务包装类呢?
每个业务包装类都需要继承一个包装类基类BaseServiceWrapper,重写doCode(即当前业务类的服务编码)与handle(详细业务处理),like this:
package com.lance.api.business.service.wrap.business;
import com.lance.api.business.service.wrap.core.BaseServiceWrapper;
import com.lance.api.common.base.model.Communication;
import com.lance.api.common.constant.ServerCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 校验包装类
*
* @author lance
*/
@Slf4j
@Component
public class CheckWrapper extends BaseServiceWrapper
{
/**
* 此业务包装类的servCode
*/
private final String servCode = ServerCode.SERVER_CODE_CHECK;
/**
* 获取当前servCode
*
* @return
*/
@Override
public String doCode()
{
return servCode;
}
/**
* 处理业务--校验
*
* @param params
* @return
* @throws Exception
*/
@Override
public Map handle(Object... params) throws Exception
{
// 返回结果map
return Communication.getInstance().getSuccessMap();
}
}
BaseServiceWrapper如下:
package com.lance.api.business.service.wrap.core;
import java.util.Map;
/**
* <p>包装类基类
*
* @author lance
* @since 2018-10-15
*/
public abstract class BaseServiceWrapper extends SameServiceWrapper
{
/**
* 获取当前servCode
*
* @return
*/
public abstract String doCode();
/**
* 传入的servCode是否为当前包装类的servCode
*
* @param doCode
* @return
*/
public Boolean isMyDo(String doCode)
{
return doCode.equals(doCode());
}
/**
* 处理业务
*
* @param params
* @return
* @throws Exception
*/
public abstract Map handle(Object... params) throws Exception;
}
ps:若服务编码一致,则需要继承SameServiceWrapper进行进一步处理。
通过ServiceWrapperContainer(包装类容器类)对比客户端传入的业务编码与包装类配置的业务编码进行比较,从容器中获取到对应的包装类
package com.lance.api.business.service.wrap.core;
import java.util.ArrayList;
import java.util.List;
/**
* <p>service包裝类容器类
*
* @author lance
* @since 2018-10-15
*/
public class ServiceWrapperContainer
{
List<BaseServiceWrapper> services = new ArrayList<>();
/**
* 添加包装类
*
* @param baseServiceWrapper
*/
public void addService(BaseServiceWrapper baseServiceWrapper)
{
services.add(baseServiceWrapper);
}
/**
* 根据servCode获取service包装类
*
* @param servCode
* @return
*/
public BaseServiceWrapper getService(String servCode)
{
for (BaseServiceWrapper baseServiceWrapper : services)
{
if (baseServiceWrapper.isMyDo(servCode))
{
return baseServiceWrapper;
}
}
return null;
}
/**
* 根据servCode获取所有service包装类
*
* @param servCode
* @return
*/
public List<BaseServiceWrapper> findSameService(String servCode)
{
List<BaseServiceWrapper> baseServiceWrapperList = new ArrayList<>();
for (BaseServiceWrapper baseServiceWrapper : services)
{
if (baseServiceWrapper.hasSameService() && baseServiceWrapper.isMyDo(servCode))
{
baseServiceWrapperList.add(baseServiceWrapper);
}
}
return baseServiceWrapperList;
}
}
通过Config将各个业务包装类存储到上述容器中
package com.lance.api.business.service.wrap.config;
import com.lance.api.business.service.wrap.business.InquiryWrapper;
import com.lance.api.business.service.wrap.business.SaveWrapper;
import com.lance.api.business.service.wrap.business.CheckWrapper;
import com.lance.api.business.service.wrap.core.ServiceWrapperContainer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <p>加载wrappers
*
* @author lance
* @since 2018-10-15
*/
@Configuration
public class InitServiceWrapper
{
@Bean("serviceWrapperContainer")
public ServiceWrapperContainer initWrapper(InquiryWrapper inquiryWrapper, SaveWrapper saveWrapper, CheckWrapper checkWrapper)
{
ServiceWrapperContainer serviceWrapper = new ServiceWrapperContainer();
serviceWrapper.addService(inquiryWrapper);
serviceWrapper.addService(saveWrapper);
serviceWrapper.addService(checkWrapper);
return serviceWrapper;
}
}
这样就可以在先前的DealSocket中通过业务编码处理对应的业务了.
测试客户端代码见下面Git地址项目的test.sslsocketclient
GItHub地址: