百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

一个成功的码农需要了解Netty与Spring Boot的整合

ztj100 2024-12-14 16:12 35 浏览 0 评论

最近有朋友向我询问一些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);
    }

    @Override
    public 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();
    /**
     * 通道适配器
     */
    @Resource
    private ServerChannelHandlerAdapter channelHandlerAdapter;
    /**
     * NETT服务器配置类
     */
    @Resource
    private NettyConfig nettyConfig;
  
    /**
     * 关闭服务器方法
     */
    @PreDestroy
    public 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>() {
                @Override
                protected 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);
 /**
     * 注入请求分排器
     */
    @Resource
    private RequestDispatcher dispatcher;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public 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);
    }

    @Override
    protected 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;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.info("客户端出异常了,异常信息:{}", cause.getMessage());
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public 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);

    }

    @Override
    public 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;
   }

   @Override
   protected 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 AbstractFactoryBean

   private Class interfaceClass;

   private NettyClient nettyClient;

   @Override
   public Class<?> getObjectType() {
       return interfaceClass;
   }

   @Override
   protected Object createInstance() throws Exception {
       logger.info("[代理工厂] 初始化代理Bean : {}", interfaceClass);
       return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, this);
   }

   @Override
   public 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

mport 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> {

   @Override
   protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) {
       byte[] data = ObjectSerializerUtils.serilizer(msg);
       ByteBuf buf = Unpooled.buffer();
       buf.writeBytes(data);
       out.add(buf);
   }

   @Override
   protected 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.util
               String filePathStr = s.substring(0, s.lastIndexOf(".*"));
               //组装路径 com/yyx/util
               String filePath = filePathStr.replaceAll("\\.", "/");
               //获取路径 xxx/classes/com/yyx/util
               File 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);
       }
   }

}

本次的内容大致的就介绍到这里拉,由于内容太多,只能简单介绍到这里,如有需要以上内容的完整版,大家可以私信我获取哦~~

相关推荐

Linux日志相关命令—查看\关键词查询\截取\日志压缩备份

一、查看1、动态日志查看。说明:程序启动可以动态查看运行日志。...

Dify+微信智能生态:手把手教你搭建私有化客服解决方案

一、Dify简介1.为什么要使用Dify?零代码门槛:通过直观界面和预设模板,非技术人员也能快速创建智能助手1。模型生态丰富...

小白入门必知必会-RocketMQ安装(rocketmq下载安装)

一RocketMQ基础1.1介绍MQ是一种提供消息队列服务的中间件,也称为消息中间件,是一套提供了消息生产、存储、消费全过程API的软件系统。RocketMQ是使用Java语言开发的一款MQ产品,...

Linux搭建Weblogic集群(linux weblogic安装与配置图文详解)

一、Java环境配置拿到新申请的两台Linux机器后,首先需要安装JDK,下载地址请戳这里。1、执行命令:rpm-qa|grepjava,查询是否存在系统自带的openjdk。如果为空,...

从0开始,让你的Spring Boot项目跑在Linux服务器

1搭建Linux服务器1.1购买阿里云服务器或安装虚拟机这里建议是CentOS7.X或CentOS8.X,当然其他的Linux如deepin、Ubuntu也可以,只是软件环境的安装包和安装方式...

搞定这8个Kafka生产级容量评估,每日10亿+请求轻松拿捏

本篇文章通过场景驱动的方式来深度剖析Kafka生产级容量评估方案如何分析,申请和实施。...

还在用nohup启动java jar服务?试试强大的systemctl吧

nohup直译过来就是不挂断,要运行后台中的nohup命令,添加&(表示“and”的符号)到命令的尾部,使用nohup启动的例子:nohup/usr/bin/java-jar/dat...

prometheus、exporter和grafana的简单使用

一、基本介绍1、prometheusPrometheus(普罗米修斯)是一套开源的监控&报警&时间序列数据库的组合,由SoundCloud公司开发。...

Mock工具之Moco使用教程(mock 工具)

目录一、什么是Moco二、安装&配置...

SpringBoot入门系列(三十)Spring Boot项目打包、发布与部署

今天介绍SpringBoot项目是如何打包、发布的。SpringBoot使用了内嵌容器,因此它的部署方式也变得非常简单灵活,一方面可以将SpringBoot项目打包成独立的jar或者war包来运...

chatgpt-on-wechat:智能对话的全新可能与开源魅力

简介chatgpt-on-wechat(简称CoW)项目是基于大模型的智能对话机器人,支持微信公众号、企业微信应用、飞书、钉钉接入,可选择GPT3.5/GPT4.0/Claude/Gemini/Lin...

免费快速实现内网穿透:windows远程桌面连接实战

本次介绍使用frp实现内网穿透,文章是之前写的,写了很多篇,后续会一一整理出来,希望帮助到有需要的朋友。frp简介...

手摸手教你 CentOS 入门必备基础知识(建议收藏)

这里记录一下我的CentOS学习过程,相当于自己记个笔记,同时分享出来,如果有同学刚好有需要而这个文章帮助到了你的话,在下也会十分开心。文章最后推介了几个免费视频,B站和慕课上的免费学习视频挺多...

分布式数据库基础性能测试(分布式数据库实验)

最近对原生HTAP(cockroachdb和tidb)数据库同时以数仓为起点做HTAP数据库的greenplum进行了相关场景性能测试,场景分为OLTP的TPC-C和TPC-B测试以及简单的OL...

使用X11VNC远程连接统信UOS(vnc 远程连接)
使用X11VNC远程连接统信UOS(vnc 远程连接)

原文链接:使用X11VNC远程连接统信UOS...

2025-05-02 14:51 ztj100

取消回复欢迎 发表评论: