Netty与Spring Boot的整合

​ 最近有朋友向我询问一些Netty与SpringBoot整合的相关问题,这里,我就总结了一下基本整合流程,也就是说,这篇文章 ,默认大家是对netty与Spring,SpringMVC的整合是没有什么问题的。现在,就进入正题吧。

Server端:

总的来说,服务端还是比较简单的,自己一共写了三个核心类。分别是

  • NettyServerListener:服务启动监听器
  • ServerChannelHandlerAdapter:通道适配器,主要用于多线程共享
  • RequestDispatcher:请求分排器

下面开始集成过程:

  1. 在pom.xml中添加以下依赖

    <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>5.0.0.Alpha2</version>
    </dependency>
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional>
    </dependency>
  2. 让SpringBoot的启动类实现CommandLineRunner接口并重写run方法,比如我的启动类是CloudApplication.java

    @SpringBootApplication
    public class CloudApplication implements CommandLineRunner {public static void main(String[] args) {SpringApplication.run(CloudApplication.class, args);}@Overridepublic void run(String... strings) {}
    }
  3. 创建类NettyServerListener.java

    // 读取yml的一个配置类
    import com.edu.hart.modules.constant.NettyConfig;
    // Netty连接信息配置类
    import com.edu.hart.modules.constant.NettyConstant;
    //
    import com.edu.hart.rpc.util.ObjectCodec;
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
    import io.netty.handler.codec.LengthFieldPrepender;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;import javax.annotation.PreDestroy;
    import javax.annotation.Resource;/*** 服务启动监听器** @author 叶云轩*/
    @Component
    public class NettyServerListener {/*** NettyServerListener 日志输出器** @author 叶云轩 create by 2017/10/31 18:05*/private static final Logger LOGGER = LoggerFactory.getLogger(NettyServerListener.class);/*** 创建bootstrap*/ServerBootstrap serverBootstrap = new ServerBootstrap();/*** BOSS*/EventLoopGroup boss = new NioEventLoopGroup();/*** Worker*/EventLoopGroup work = new NioEventLoopGroup();/*** 通道适配器*/@Resourceprivate ServerChannelHandlerAdapter channelHandlerAdapter;/*** NETT服务器配置类*/@Resourceprivate NettyConfig nettyConfig;/*** 关闭服务器方法*/@PreDestroypublic void close() {LOGGER.info("关闭服务器....");//优雅退出boss.shutdownGracefully();work.shutdownGracefully();}/*** 开启及服务线程*/public void start() {// 从配置文件中(application.yml)获取服务端监听端口号int port = nettyConfig.getPort();serverBootstrap.group(boss, work).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100).handler(new LoggingHandler(LogLevel.INFO));try {//设置事件处理serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new LengthFieldBasedFrameDecoder(nettyConfig.getMaxFrameLength(), 0, 2, 0, 2));pipeline.addLast(new LengthFieldPrepender(2));pipeline.addLast(new ObjectCodec());pipeline.addLast(channelHandlerAdapter);}});LOGGER.info("netty服务器在[{}]端口启动监听", port);ChannelFuture f = serverBootstrap.bind(port).sync();f.channel().closeFuture().sync();} catch (InterruptedException e) {LOGGER.info("[出现异常] 释放资源");boss.shutdownGracefully();work.shutdownGracefully();}}
    }
  4. 创建类ServerChannelHandlerAdapter.java - 通道适配器

    // 记录调用方法的元信息的类
    import com.edu.hart.rpc.entity.MethodInvokeMeta;
    import io.netty.channel.ChannelHandler.Sharable;
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;import javax.annotation.Resource;/*** 多线程共享*/
    @Component
    @Sharable
    public class ServerChannelHandlerAdapter extends ChannelHandlerAdapter {/*** 日志处理*/private Logger logger = LoggerFactory.getLogger(ServerChannelHandlerAdapter.class);/*** 注入请求分排器*/@Resourceprivate RequestDispatcher dispatcher;@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {MethodInvokeMeta invokeMeta = (MethodInvokeMeta) msg;// 屏蔽toString()方法if (invokeMeta.getMethodName().endsWith("toString()")&& !"class java.lang.String".equals(invokeMeta.getReturnType().toString()))logger.info("客户端传入参数 :{},返回值:{}",invokeMeta.getArgs(), invokeMeta.getReturnType());dispatcher.dispatcher(ctx, invokeMeta);}
    }
  5. RequestDispatcher.java

    // 封装的返回信息枚举类
    import com.edu.hart.modules.communicate.ResponseCodeEnum;
    // 封装的返回信息实体类
    import com.edu.hart.modules.communicate.ResponseResult;
    // 封装的连接常量类
    import com.edu.hart.modules.constant.NettyConstant;
    // 记录元方法信息的实体类
    import com.edu.hart.rpc.entity.MethodInvokeMeta;
    // 对于返回值为空的一个处理
    import com.edu.hart.rpc.entity.NullWritable;
    // 封装的返回信息实体工具类
    import com.edu.hart.rpc.util.ResponseResultUtil;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandlerContext;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;import java.lang.reflect.Method;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;/*** 请求分排器*/
    @Component
    public class RequestDispatcher implements ApplicationContextAware {private ExecutorService executorService = Executors.newFixedThreadPool(NettyConstant.getMaxThreads());private ApplicationContext app;/*** 发送** @param ctx* @param invokeMeta*/public void dispatcher(final ChannelHandlerContext ctx, final MethodInvokeMeta invokeMeta) {executorService.submit(() -> {ChannelFuture f = null;try {Class<?> interfaceClass = invokeMeta.getInterfaceClass();String name = invokeMeta.getMethodName();Object[] args = invokeMeta.getArgs();Class<?>[] parameterTypes = invokeMeta.getParameterTypes();Object targetObject = app.getBean(interfaceClass);Method method = targetObject.getClass().getMethod(name, parameterTypes);Object obj = method.invoke(targetObject, args);if (obj == null) {f = ctx.writeAndFlush(NullWritable.nullWritable());} else {f = ctx.writeAndFlush(obj);}f.addListener(ChannelFutureListener.CLOSE);} catch (Exception e) {ResponseResult error = ResponseResultUtil.error(ResponseCodeEnum.SERVER_ERROR);f = ctx.writeAndFlush(error);} finally {f.addListener(ChannelFutureListener.CLOSE);}});}/*** 加载当前application.xml** @param ctx* @throws BeansException*/public void setApplicationContext(ApplicationContext ctx) throws BeansException {this.app = ctx;}
    }
  6. application.yml文件中对于netty的一个配置

    netty:port: 11111
  7. NettyConfig.java

    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;/*** 读取yml配置文件中的信息* Created by 叶云轩 on 2017/10/31 - 18:38* Concat tdg_yyx@foxmail.com*/
    @Component
    @ConfigurationProperties(prefix = "netty")
    public class NettyConfig {private int port;public int getPort() {return port;}public void setPort(int port) {this.port = port;}
    }
  8. NettyConstanct.java

    import org.springframework.stereotype.Component;/*** Netty服务器常量* Created by 叶云轩 on 2017/10/31 - 17:47* Concat tdg_yyx@foxmail.com*/
    @Component
    public class NettyConstant {/*** 最大线程量*/private static final int MAX_THREADS = 1024;/*** 数据包最大长度*/private static final int MAX_FRAME_LENGTH = 65535;public static int getMaxFrameLength() {return MAX_FRAME_LENGTH;}public static int getMaxThreads() {return MAX_THREADS;}
    }

    至此,netty服务端算是与SpringBoot整合成功。那么看一下启动情况吧。

Client端:

Client我感觉要比Server端要麻烦一点。这里还是先给出核心类吧。

  • NettyClient : netty客户端
  • ClientChannelHandlerAdapter : 客户端通道适配器
  • CustomChannelInitalizer:自定义通道初始化工具
  • RPCProxyFactoryBean:RPC通信代理工厂

在Client端里。SpringBoot的启动类要继承SpringBootServletInitializer这个类,并覆盖SpringApplicationBuilder方法

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;@SpringBootApplication
public class OaApplication extends SpringBootServletInitializer {public static void main(String[] args) {SpringApplication.run(OaApplication.class, args);}@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(OaApplication.class);}
}
  1. NettyClient.java

    // 记录元方法信息的实体类
    import com.edu.hart.rpc.entity.MethodInvokeMeta;
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;import javax.management.MBeanServer;/*** 客户端发送类* Created by 叶云轩 on 2017/6/16-16:58* Concat tdg_yyx@foxmail.com*/
    public class NettyClient {private Logger logger = LoggerFactory.getLogger(MBeanServer.class);private Bootstrap bootstrap;private EventLoopGroup worker;private int port;private String url;private int MAX_RETRY_TIMES = 10;public NettyClient(String url, int port) {this.url = url;this.port = port;bootstrap = new Bootstrap();worker = new NioEventLoopGroup();bootstrap.group(worker);bootstrap.channel(NioSocketChannel.class);}public void close() {logger.info("关闭资源");worker.shutdownGracefully();}public Object remoteCall(final MethodInvokeMeta cmd, int retry) {try {CustomChannelInitializerClient customChannelInitializer = new CustomChannelInitializerClient(cmd);bootstrap.handler(customChannelInitializer);ChannelFuture sync = bootstrap.connect(url, port).sync();sync.channel().closeFuture().sync();Object response = customChannelInitializer.getResponse();return response;} catch (InterruptedException e) {retry++;if (retry > MAX_RETRY_TIMES) {throw new RuntimeException("调用Wrong");} else {try {Thread.sleep(100);} catch (InterruptedException e1) {e1.printStackTrace();}logger.info("第{}次尝试....失败", retry);return remoteCall(cmd, retry);}}}
    }
  2. ClientChannelHandlerAdapter.java

    import com.edu.hart.rpc.entity.MethodInvokeMeta;
    import io.netty.channel.ChannelHandlerAdapter;
    import io.netty.channel.ChannelHandlerContext;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;/*** Created by 叶云轩 on 2017/6/16-17:03* Concat tdg_yyx@foxmail.com*/
    public class ClientChannelHandlerAdapter extends ChannelHandlerAdapter {private Logger logger = LoggerFactory.getLogger(ClientChannelHandlerAdapter.class);private MethodInvokeMeta methodInvokeMeta;private CustomChannelInitializerClient channelInitializerClient;public ClientChannelHandlerAdapter(MethodInvokeMeta methodInvokeMeta, CustomChannelInitializerClient channelInitializerClient) {this.methodInvokeMeta = methodInvokeMeta;this.channelInitializerClient = channelInitializerClient;}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {logger.info("客户端出异常了,异常信息:{}", cause.getMessage());cause.printStackTrace();ctx.close();}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {if (methodInvokeMeta.getMethodName().endsWith("toString") && !"class java.lang.String".equals(methodInvokeMeta.getReturnType().toString()))logger.info("客户端发送信息参数:{},信息返回值类型:{}", methodInvokeMeta.getArgs(), methodInvokeMeta.getReturnType());ctx.writeAndFlush(methodInvokeMeta);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {channelInitializerClient.setResponse(msg);}
    }
  3. CustomChannelInitializerClient.java

    import com.edu.hart.rpc.entity.MethodInvokeMeta;
    import com.edu.hart.rpc.entity.NullWritable;
    import com.edu.hart.rpc.util.ObjectCodec;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
    import io.netty.handler.codec.LengthFieldPrepender;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    /**

    • Created by 叶云轩 on 2017/6/16-15:01
    • Concat tdg_yyx@foxmail.com
      */
      public class CustomChannelInitializerClient extends ChannelInitializer {
   private Logger logger = LoggerFactory.getLogger(CustomChannelInitializerClient.class);private MethodInvokeMeta methodInvokeMeta;private Object response;public CustomChannelInitializerClient(MethodInvokeMeta methodInvokeMeta) {if (!"toString".equals(methodInvokeMeta.getMethodName())) {logger.info("[CustomChannelInitializerClient] 调用方法名:{},入参:{},参数类型:{},返回值类型{}", methodInvokeMeta.getMethodName(), methodInvokeMeta.getArgs(), methodInvokeMeta.getParameterTypes(), methodInvokeMeta.getReturnType());}this.methodInvokeMeta = methodInvokeMeta;}public Object getResponse() {if (response instanceof NullWritable) {return null;}return response;}public void setResponse(Object response) {this.response = response;}@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new LengthFieldPrepender(2));pipeline.addLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 2, 0, 2));pipeline.addLast(new ObjectCodec());pipeline.addLast(new ClientChannelHandlerAdapter(methodInvokeMeta, this));}

}

4. RPCProxyFactoryBean.java

import com.edu.hart.rpc.entity.MethodInvokeMeta;
import com.edu.hart.rpc.util.WrapMethodUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* Created by 叶云轩 on 2017/6/16-17:16
* Concat tdg_yyx@foxmail.com
*/
public class RPCProxyFactoryBean extends AbstractFactoryBeanimplements InvocationHandler {
private Logger logger = LoggerFactory.getLogger(RPCProxyFactoryBean.class);

   private Class interfaceClass;private NettyClient nettyClient;@Overridepublic Class<?> getObjectType() {return interfaceClass;}@Overrideprotected Object createInstance() throws Exception {logger.info("[代理工厂] 初始化代理Bean : {}", interfaceClass);return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) {final MethodInvokeMeta methodInvokeMeta = WrapMethodUtils.readMethod(interfaceClass, method, args);if (!methodInvokeMeta.getMethodName().equals("toString")) {logger.info("[invoke] 调用接口{},调用方法名:{},入参:{},参数类型:{},返回值类型{}",methodInvokeMeta.getInterfaceClass(), methodInvokeMeta.getMethodName(), methodInvokeMeta.getArgs(), methodInvokeMeta.getParameterTypes(), methodInvokeMeta.getReturnType());}return nettyClient.remoteCall(methodInvokeMeta, 0);}public void setInterfaceClass(Class interfaceClass) {this.interfaceClass = interfaceClass;}public void setNettyClient(NettyClient nettyClient) {this.nettyClient = nettyClient;}

}

至此,netty-client与SpringBoot的集成了算完毕了。同样 ,在netty-client中也要加入相应的依赖不过上面server与client使用了一些公共的类和工具。下面也给列举中出来。###### MethodInvokeMeta.java

import org.springframework.stereotype.Component;

import java.io.Serializable;

/**
* 记录调用方法的元信息
* Created by 叶云轩 on 2017/6/7-15:41
* Concat tdg_yyx@foxmail.com
*/
@Component
public class MethodInvokeMeta implements Serializable {

   private static final long serialVersionUID = 8379109667714148890L;//接口private Class<?> interfaceClass;//方法名private String methodName;//参数private Object[] args;//返回值类型private Class<?> returnType;//参数类型private Class<?>[] parameterTypes;public Object[] getArgs() {return args;}public void setArgs(Object[] args) {this.args = args;}public Class<?> getInterfaceClass() {return interfaceClass;}public void setInterfaceClass(Class<?> interfaceClass) {this.interfaceClass = interfaceClass;}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}public Class[] getParameterTypes() {return parameterTypes;}public void setParameterTypes(Class<?>[] parameterTypes) {this.parameterTypes = parameterTypes;}public Class getReturnType() {return returnType;}public void setReturnType(Class returnType) {this.returnType = returnType;}

}


###### NullWritable.java

import java.io.Serializable;

/**
* 服务器可能返回空的处理
* Created by 叶云轩 on 2017/6/16-16:46
* Concat tdg_yyx@foxmail.com
*/
public class NullWritable implements Serializable {

   private static final long serialVersionUID = -8191640400484155111L;private static NullWritable instance = new NullWritable();private NullWritable() {}public static NullWritable nullWritable() {return instance;}

}


###### ObjectCodec.java

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;

import java.util.List;

public class ObjectCodec extends MessageToMessageCodec<ByteBuf, Object> {

   @Overrideprotected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) {byte[] data = ObjectSerializerUtils.serilizer(msg);ByteBuf buf = Unpooled.buffer();buf.writeBytes(data);out.add(buf);}@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) {byte[] bytes = new byte[msg.readableBytes()];msg.readBytes(bytes);Object deSerilizer = ObjectSerializerUtils.deSerilizer(bytes);out.add(deSerilizer);}

}


###### ObjectSerializerUtils.java

package com.edu.hart.rpc.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
* 对象序列化工具
*/
public class ObjectSerializerUtils {

   private static final Logger logger = LoggerFactory.getLogger(ObjectSerializerUtils.class);/*** 反序列化** @param data* @return*/public static Object deSerilizer(byte[] data) {if (data != null && data.length > 0) {try {ByteArrayInputStream bis = new ByteArrayInputStream(data);ObjectInputStream ois = new ObjectInputStream(bis);return ois.readObject();} catch (Exception e) {logger.info("[异常信息] {}", e.getMessage());e.printStackTrace();}return null;} else {logger.info("[反序列化] 入参为空");return null;}}/*** 序列化对象** @param obj* @return*/public static byte[] serilizer(Object obj) {if (obj != null) {try {ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(obj);oos.flush();oos.close();return bos.toByteArray();} catch (IOException e) {e.printStackTrace();}return null;} else {return null;}}

}

下面主要是用于Client端的:###### NettyBeanSacnner.java

import com.edu.hart.rpc.client.RPCProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

import java.util.List;

/**
* 动态加载代理bean到Spring bean工厂
*/
public class NettyBeanScanner implements BeanFactoryPostProcessor {

   private DefaultListableBeanFactory beanFactory;private String basePackage;private String clientName;public NettyBeanScanner(String basePackage, String clientName) {this.basePackage = basePackage;this.clientName = clientName;}/*** 注册Bean到Spring的bean工厂*/public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {this.beanFactory = (DefaultListableBeanFactory) beanFactory;// 加载远程服务的接口List<String> resolverClass = PackageClassUtils.resolver(basePackage);for (String clazz : resolverClass) {String simpleName;if (clazz.lastIndexOf('.') != -1) {simpleName = clazz.substring(clazz.lastIndexOf('.') + 1);} else {simpleName = clazz;}BeanDefinitionBuilder gd = BeanDefinitionBuilder.genericBeanDefinition(RPCProxyFactoryBean.class);gd.addPropertyValue("interfaceClass", clazz);gd.addPropertyReference("nettyClient", clientName);this.beanFactory.registerBeanDefinition(simpleName, gd.getRawBeanDefinition());}}

}


###### PackageClassUtils.java**这个类要说一下,主要是用来加载Server对应的接口的。因为在Client中RPC接口没有实现类,所以要自己将这些接口加载到Spring工厂里面。但是现在有个问题就是需要使用**###### SpringBoot中application.yml

basePackage: com.edu.hart.rpc.service.login;com.edu.hart.rpc.service.employee;com.edu.hart.rpc.service.authorization;

**这样的方式来加载,使用通配符的时候会加载不到,这个问题我还没有解决。**

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
* 字节文件加载
*/
public class PackageClassUtils {

   private final static Logger LOGGER = LoggerFactory.getLogger(PackageClassUtils.class);/*** 解析包参数** @param basePackage 包名* @return 包名字符串集合*/public static List<String> resolver(String basePackage) {//以";"分割开多个包名String[] splitFHs = basePackage.split(";");List<String> classStrs = new ArrayList<>();//s: com.yyx.util.*for (String s : splitFHs) {LOGGER.info("[加载类目录] {}", s);//路径中是否存在".*" com.yyx.util.*boolean contains = s.contains(".*");if (contains) {//截断星号  com.yyx.utilString filePathStr = s.substring(0, s.lastIndexOf(".*"));//组装路径 com/yyx/utilString filePath = filePathStr.replaceAll("\\.", "/");//获取路径 xxx/classes/com/yyx/utilFile file = new File(PackageClassUtils.class.getResource("/").getPath() + "/" + filePath);//获取目录下获取文件getAllFile(filePathStr, file, classStrs);} else {String filePath = s.replaceAll("\\.", "/");File file = new File(PackageClassUtils.class.getResource("/").getPath() + "/" + filePath);classStrs = getClassReferenceList(classStrs, file, s);}}return classStrs;}/*** 添加全限定类名到集合** @param classStrs 集合* @return 类名集合*/private static List<String> getClassReferenceList(List<String> classStrs, File file, String s) {File[] listFiles = file.listFiles();if (listFiles != null && listFiles.length != 0) {for (File file2 : listFiles) {if (file2.isFile()) {String name = file2.getName();String fileName = s + "." + name.substring(0, name.lastIndexOf('.'));LOGGER.info("[加载完成] 类文件:{}", fileName);classStrs.add(fileName);}}}return classStrs;}/*** 获取一个目录下的所有文件** @param s* @param file* @param classStrs*/private static void getAllFile(String s, File file, List<String> classStrs) {if (file.isDirectory()) {File[] files = file.listFiles();if (files != null)for (File file1 : files) {getAllFile(s, file1, classStrs);}} else {String path = file.getPath();String cleanPath = path.replaceAll("/", ".");String fileName = cleanPath.substring(cleanPath.indexOf(s), cleanPath.length());LOGGER.info("[加载完成] 类文件:{}", fileName);classStrs.add(fileName);}}

}


###### RemoteMethodInvokeUtil.java

import com.edu.hart.rpc.entity.MethodInvokeMeta;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* 消息处理类
* Created by 叶云轩 on 2017/6/7-15:49
* Concat tdg_yyx@foxmail.com
*/
public class RemoteMethodInvokeUtil implements ApplicationContextAware {

   private ApplicationContext applicationContext;public Object processMethod(MethodInvokeMeta methodInvokeMeta) throws InvocationTargetException, IllegalAccessException {Class interfaceClass = methodInvokeMeta.getInterfaceClass();Object bean = applicationContext.getBean(interfaceClass);Method[] declaredMethods = interfaceClass.getDeclaredMethods();Method method = null;for (Method declaredMethod : declaredMethods) {if (methodInvokeMeta.getMethodName().equals(declaredMethod.getName())) {method = declaredMethod;}}Object invoke = method.invoke(bean, methodInvokeMeta.getArgs());return invoke;}@Overridepublic void setApplicationContext(ApplicationContext app) throws BeansException {applicationContext = app;}

}


###### WrapMethodUtils.java

import com.edu.hart.rpc.entity.MethodInvokeMeta;

import java.lang.reflect.Method;

public class WrapMethodUtils {
/**
* 获取 method的元数据信息

@param interfaceClass
* @param method
* @param args
* @return
*/
public static MethodInvokeMeta readMethod(Class interfaceClass, Method method, Object[] args) {
MethodInvokeMeta mim = new MethodInvokeMeta();
mim.setInterfaceClass(interfaceClass);
mim.setArgs(args);
mim.setMethodName(method.getName());
mim.setReturnType(method.getReturnType());
Class<?>[] parameterTypes = method.getParameterTypes();
mim.setParameterTypes(parameterTypes);
return mim;
}
}


------下面的这些类我也会用在与前台通信时使用:###### ResponseEnum.java

import java.io.Serializable;

/**

  • 响应码枚举类
  • Created by 叶云轩 on 2017/6/13-11:53
  • Concat tdg_yyx@foxmail.com
    */
    public enum ResponseCodeEnum implements Serializable {

    // region authentication code
    REQUEST_SUCCESS(10000, "请求成功"),
    SERVER_ERROR(99999, "服务器内部错误"),;

    //region 提供对外访问的方法,无需更改
    /**

    • 响应码
      */
      private Integer code;
      /**
    • 响应信息
      */
      private String msg;

    ResponseCodeEnum(Integer code, String msg) {
    this.code = code;
    this.msg = msg;
    }

    public Integer getCode() {
    return code;
    }

    public String getMsg() {
    return msg;
    }

    //endregion
    }
    ```

ResponseResult.java

import java.io.Serializable;/*** 数据返回实体封装* <p>* Created by 叶云轩 on 2017/6/13-11:38* Concat tdg_yyx@foxmail.com** @param <T> 通用变量*/
public class ResponseResult<T> implements Serializable {private static final long serialVersionUID = -3411174924856108156L;/*** 服务器响应码*/private Integer code;/*** 服务器响应说明*/private String msg;/*** 服务器响应数据*/private T data;public ResponseResult() {}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;ResponseResult<?> that = (ResponseResult<?>) o;return (code != null ? code.equals(that.code) : that.code == null) && (msg != null ? msg.equals(that.msg) : that.msg == null) && (data != null ? data.equals(that.data) : that.data == null);}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public T getData() {return data;}public void setData(T data) {this.data = data;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}@Overridepublic int hashCode() {int result = code != null ? code.hashCode() : 0;result = 31 * result+ (msg != null ? msg.hashCode() : 0);result = 31 * result + (data != null ? data.hashCode() : 0);return result;}@Overridepublic String toString() {return "ResponseResult{"+ "code="+ code+ ", msg='"+ msg+ '\''+ ", data="+ data+ '}';}
}

ResponseResultUtil.java

import com.edu.hart.modules.communicate.ResponseCodeEnum;
import com.edu.hart.modules.communicate.ResponseResult;/*** 返回结果工具类* Created by 叶云轩 on 2017/5/29-10:37* Concat tdg_yyx@foxmail.com*/
public class ResponseResultUtil {/*** 请求失败返回的数据结构** @param responseCodeEnum 返回信息枚举类* @return 结果集*/public static ResponseResult error(ResponseCodeEnum responseCodeEnum) {ResponseResult ResponseResult = new ResponseResult();ResponseResult.setMsg(responseCodeEnum.getMsg());ResponseResult.setCode(responseCodeEnum.getCode());ResponseResult.setData(null);return ResponseResult;}/*** 没有结果集的返回数据结构** @return 结果集*/public static ResponseResult success() {return success(null);}/*** 成功返回数据结构** @param o 返回数据对象* @return 返回结果集*/public static ResponseResult success(Object o) {ResponseResult responseResult = new ResponseResult();responseResult.setMsg(ResponseCodeEnum.REQUEST_SUCCESS.getMsg());responseResult.setCode(ResponseCodeEnum.REQUEST_SUCCESS.getCode());responseResult.setData(o);return responseResult;}/*** 判断是否成功** @param responseResult 请求结果* @return 判断结果*/public static boolean judgementSuccess(ResponseResult responseResult) {return responseResult.getCode().equals(ResponseCodeEnum.REQUEST_SUCCESS.getCode());}
}

来,我们测试一下远程通信:

  1. Client调用Server的一个接口。可以看到在hart-oa项目中,RPCEmployeeService没有任何实现类,控制台中打印了方法的调用 以及入参信息

  1. Server断点监听到远程调用,CloudApplication项目为Server端,我们可以看到接收到来自hart-oa的一个请求,参数一致。在CloudApplication中进行相应的处理后,返回到Client(hart-oa)

  1. 返回信息到Client,可以看到我们(hart-oa)收到了来自CloudApplication的响应,结果是我们封装好的ResponseResult.


嗯 ~至此整合测试完成。

Netty与SpringBoot整合相关推荐

  1. Netty(一) SpringBoot 整合长连接心跳机制

    https://github.com/crossoverJie/JCSprout 原创: crossoverJie 阅读原文 前言 Netty 是一个高性能的 NIO 网络框架,本文基于 Spring ...

  2. springboot整合netty

    前面介绍了netty的基本使用以及和websocket的整合,下面就说说如何用springboot整合netty,毕竟我们是要把netty作为一个服务端的框架整合到我们的项目中去的,总不能用main函 ...

  3. Springboot 整合 Netty 实战(附源码)

    作者:pjmike_pj juejin.im/post/5bd584bc518825292865395d 前言 这一篇文章主要介绍如何用Springboot 整合 Netty,由于本人尚处于学习Net ...

  4. Springboot整合Netty,实现Socket通信

    文章目录 *Springboot整合Netty,实现Socket通信* 1.模拟单客户端 2.模拟单服务端 总结 Springboot整合Netty,实现Socket通信 1.模拟单客户端 引入Net ...

  5. SpringBoot 整合 Netty

    SpringBoot整合Netty 一.common工程 1.maven依赖 2.自定义Netty数据包类型(NettyPacketType) 3.自定义Netty数据包(NettyPacket) 4 ...

  6. 关于SpringBoot整合Netty客户端和服务端实现JT808协议

    关于SpringBoot整合Netty客户端和服务端实现JT808协议 最近做了一个使用netty实现交通部JT808协议的项目,对比了mina和netty两种框架的使用,先整理一下netty的实现过 ...

  7. netty框架学习及springboot整合集成

    netty框架学习及springboot整合集成 1. Netty基本概念 2. Netty框架 2.1 Netty框架结构 2.1 Netty NIO 2.2 Reactor线程模型 3. Spri ...

  8. Springboot整合redis(lettuce)

    springboot 整合redis(lettuce) 首先确保电脑上装了redis.最好能用redisDesktop查看一下数据情况 redis是一款非常流行的Nosql数据库.redis的功能非常 ...

  9. maven netty 配置_SpringBoot整合Netty(附源码)

    前言 本篇文章主要介绍的是SpringBoot整合Netty以及使用Protobuf进行数据传输的相关内容.Protobuf会简单的介绍下用法,至于Netty在之前的文章中已经简单的介绍过了,这里就不 ...

最新文章

  1. [git]一个本地仓库,多个远程仓库
  2. 1.2 检测和测量图像中的圆形目标
  3. java定时任务_定时任务最简单的3种实现方法(超好用)
  4. HarmonyOS之AI能力·分词
  5. 数据结构之平衡树:2-3查找树的介绍——16
  6. php 修改excel内容吗,php更新修改excel中的内容例子
  7. 深度学习之卷积神经网络CNN
  8. js创建,删除,读取文件目录_note
  9. java 虚拟内存 堆_jvm虚拟内存分布 与 GC算法
  10. dma和通道的区别_Java中IO和NIO的本质和区别
  11. HCIE Security 防火墙用户管理与认证 备考笔记(幕布)
  12. 【转载】Altera官方资料整理
  13. 2022-LaTex最新官网安装教程
  14. 关于学习的反思(上)---系网开发记(2)
  15. Jasperreports5.6支持PDF微软雅黑字体
  16. [面试必考]OSI 网络七层协议以及各层的功能
  17. 要怎么在计算机里清除桌面内存,告诉你电脑内存怎么清理
  18. 英语:逆向忠言(转载)
  19. MachineLearning in Action (机器学习实战)源码和数据集下载地址
  20. 信度和效度经典例子_(完整版)心理学中的各种信度和效度

热门文章

  1. 2022年推荐消防标准规范汇编自动喷水灭火系统消防设施标志设计规程(附件中为网盘链接),共267份,1.75G
  2. Python综合案例2(险种缴费记录管理)
  3. 【读书笔记】《利用Python进行数据分析》第2版_第六章 数据载入、存储及文件格式
  4. MSM8974 TP驱动流程
  5. 基于微信小程序的新生自助报到系统设计与实现-计算机毕业设计源码+LW文档
  6. 小家电项目硬件方案分析和报价
  7. bi 建模流程图_搞懂PowerBI的数据建模
  8. 【厚积薄发系列】读书笔记3—《麦肯锡-问题分析与解决技巧》小记
  9. 2023,开启「线控转向」元年
  10. 远程视频监控:流媒体之FFmpeg+RTMP+Nginx+VLC