范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

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

  最近有朋友向我询问一些Netty与SpringBoot整合的相关问题,这里,我就总结了一下基本整合流程,也就是说,这篇文章 ,默认大家是对netty与Spring,SpringMVC的整合是没有什么问题的。现在,就进入正题吧。 Server端:#
  总的来说,服务端还是比较简单的,自己一共写了三个核心类。分别是 NettyServerListener:服务启动监听器 ServerChannelHandlerAdapter:通道适配器,主要用于多线程共享 RequestDispatcher:请求分排器
  下面开始集成过程: 在pom.xml中添加以下依赖    io.netty   netty-all   5.0.0.Alpha2      org.springframework.boot    spring-boot-configuration-processor    true 
  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() {                 @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 {    @Override    protected void encode(ChannelHandlerContext ctx, Object msg, List 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 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 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 resolver(String basePackage) {        //以";"分割开多个包名        String[] splitFHs = basePackage.split(";");        List 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 getClassReferenceList(List 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 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);        }    }
  }
  本次的内容大致的就介绍到这里拉,由于内容太多,只能简单介绍到这里,如有需要以上内容的完整版,大家可以私信我获取哦~~
丁俊晖单打首败无碍大局,陕西有惊无险过关,晋级全国团体赛4强北京时间7月8日19点30分,2022全国斯诺克团体赛在西安展开8强阶段对决。丁俊晖所在的东道主陕西队4比2击败中山队,晋级半决赛。丁俊晖遭遇了本届比赛开始以来的首次单打失利,不过2022下半年,坚持做到这3点不知不觉,2022年已经过去一半,这半年,你过得怎么样?回想过去的半年,不管是缺憾失落,还是满足高兴,过去的都已经过去了。未来,愿你继续好好努力,好好生活,好好说话,去遇见最好的自华为Mate50有望下半年登场骁龙8Gen1鸿蒙3。0,配置尴尬?今年菊粉都在盼着华为Mate50的到来,不出意外的话,新机会在今年下半年亮相,将骁龙8Gen1处理器鸿蒙3。0系统。据悉华为Mate50系列依旧会保留曲面屏设计,其中Mate50采下半年这3款128G性价比手机,最低仅1280元,用五年都没压力说了很多次买手机只买对的不买贵的,性价比手机才是最优选择,今天给大家推荐下半年即将降价的3款性价比手机,准备捡漏的朋友赶紧码住第一款红米k40虽然已经退市了,但是在二手市场上,红米上赛季仅7人完成2555,裁掉1人,1人替补,5人首发,应该怎么选根据统计,20212022赛季NBA常规赛一共只有7个人完成了场均25分5篮板5助攻的全面表现,其中包括莫兰特,而莫兰特本人也转发了这个消息,并配上精英队伍。这7人分别是约基奇字母示好勇士队!7000万先生引多队哄抢渴望底薪联手库里因为多方面的影响,波特兰开拓者队一直无法在季后赛中取得佳绩,所以管理层先是解雇了球队主教练斯托茨,接近着又在2122赛季交易截止日期间送走了二当家麦科勒姆,甚至连鲍威尔和考文顿一众益生菌三兄弟妈咪爱金双歧思连康,到底怎么选?作者大连市妇女儿童医疗中心集团体育新城院区主管药师于红妈咪爱金双歧思连康分别叫二联三联四联活菌片,他们有什么区别?哪种好?是不是益生菌数量越多越好?面对市面上琳琅满目种类繁多的益生两岁的娃狗都嫌,你对症下药了吗?你让她往东,她就往西。你让她站好,她就满地打滚。不不不NONONO不要不要不要得不到就崩溃,想做的事情就一定要做,日常暴风哭泣,沉浸式持续性根本停不下来,抓猫逗鸟,鸡飞狗跳。这就是江苏新一批特色田园乡村名单来了江苏省农村住房条件改善和特色田园乡村建设工作联席会议日前印发关于命名第九批次江苏省特色田园乡村的通知,正式命名南京市浦口区星甸街道九华村山藤等94个村庄为江苏省特色田园乡村。至此,摘星后你最想去哪里?我最想去西藏!6月底,工信部宣布取消通信行程卡星号标记虽然行程卡摘星并不意味着可以随便浪!但在科学防疫动态清零的政策下极大地方便了广大用户出行行程卡摘星消息一经发布立马各种花式登上微博热搜而小编炎炎夏日,沙漠里顽强的生命力盛夏已至,全国大部分地区基本都开启了高温炙烤模式,炎热成为统一的主格调,而一向凉爽的内蒙古高原的最高气温也达到35摄氏度左右,也进入了一年中最炎热的时候,内蒙古的沙漠更是进入了火炉
神舟十三号飞行任务发布会,天宫空间站合作者名单公布,没有美国万众瞩目的神舟十三号载人飞船,经由总指挥部研究决定,于北京时间10月16号0时23分发射,此次飞行乘组将由翟志刚王亚平和叶光富组成,翟志刚担任指令长。此次神舟十三号载人飞船创下了三女航天员王亚平再出征,网友期待天地连线网课和出舱作业钱江晚报小时新闻记者陈伟斌刚刚,据中国载人航天工程办公室消息,经空间站阶段飞行任务总指挥部研究决定,翟志刚王亚平叶光富3名航天员将执行神舟十三号载人飞行任务,由翟志刚担任指令长。王请给旅游业留点底火自新冠疫情去年初爆发至今,从前公认朝阳工业旅游工作,被冲击的失掉节奏,改动既有规律,更是彻底地依照防疫的节奏来开展。不仅如此,因为疫情呈点状爆发,偌大个工业还随时预备归零。在此情况名城青山下碧流,江树引舒州长三角这座城市有戏剧之乡的美誉ANQING安庆东晋诗人郭璞曾称此地宜城,故安庆又别名宜城。东周时期安庆是古皖国所在地,安徽省简称皖即由此而来。南宋绍兴十七年,改舒州德庆军为舒州安庆军,安庆自此得名。安庆,位于安北京又一景点走红,号称怀柔后花园,景美人少建议自驾后花园本来指的就是在房子后面建造的普通的花园,供人们休息用的。但是,它其实也可以用来形容这个地方风景美如画,因此可以称之为这个地方的后花园。走进花园,那里的花争奇斗艳五彩缤纷,有紫新疆喀纳斯湖,成吉思汗葬在湖中?新疆的喀纳斯湖曾因为水怪传说而被很多人关注,同时还有一个说法成吉思汗铁木真死后,就葬在喀纳斯湖中。我们今天就来了解一下这个神秘的湖泊。新疆位于我国西部,与蒙古俄罗斯哈萨克斯坦吉尔吉慢性咽炎,一种如鲠在喉的痛如果用一个词来形容慢性咽炎,那就是如鲠在喉。喉咙干痒,觉得有东西却咽不下去早晨刷牙恶心,却吐不出来东西慢性咽炎虽不是大病重病,但因其发病率高,患病人数多,容易被轻视等原因,往往会影长期吸烟的人,上半身若出现5种表现,或需要通过戒烟来养肺了吸烟可以让人放下烦恼,应酬的时候来上几根香烟可以提高气氛,所以让很多人明明知道吸烟有害身体健康,却还是放不下香烟。长期吸烟,香烟当中含有的尼古丁和其他有害物质会伤害到肺部,近几年来不管男女,每天喝上一碗开水冲鸡蛋,或许会带来3个好处鸡蛋是一种营养价值非常高的家常食物,价格比较便宜,一直以来深受人们的喜爱。很多朋友们平常喜欢用开水冲鸡蛋吃,他们认为这样吃鸡蛋营养价值更高,更能保护身体健康。但是有些人却感觉这样吃身体出现4大表现,代表受损肝脏正在恢复,做好2件事可加速恢复乙肝,大部分人心头挥之不去的一个词汇,它的危害性,大家或多或少都清楚,许多家庭都是因为它而支离破碎,但大家都知道目前全直接还有没有治疗的乙肝的特效药。难道患上乙肝后,就真的不能康复节日话题重阳节九月九日眺山川,战地黄花分外香节日话题重阳节的来历和习俗重阳节诗词九九重阳节,养生需注意!编者按节日,是指生活中值得纪念的重要日子。是世界人民为适应生产和生活的需要而共同创造的一种民俗文化,是世界民俗文化的重要