消息 ByteBuf 详解

2023-10-27

Netty提供了ByteBuf来替代Java NIO的ByteBuffer缓冲区,以操纵内存缓冲区。

与Java NIO的ByteBuffer相比,ByteBuf的优势如下:

· Pooling(池化,这点减少了内存复制和GC,提升了效率)
·复合缓冲区类型,支持零复制
· 不需要调用flip()方法去切换读/写模式
·扩展性好,例如StringBuffer
·可以自定义缓冲区类型
·读取和写入索引分开
·方法的链式调用
·可以进行引用计数,方便重复使用
ByteBuf是一个字节容器,内部是一个字节数组。从逻辑上来分,字节容器内部可以分为四个部分:
第一个部分是已用字节,表示已经使用完的废弃的无效字节;
第二部分是可读字节,这部分数据是ByteBuf保存的有效数据,从ByteBuf中读取的数据都来自这一部分;
第三部分是可写字节,写入到ByteBuf的数据都会写到这一部分中;
第四部分是可扩容字节,表示的是该ByteBuf最多还能扩容的大小。

ByteBuf的重要属性:

ByteBuf通过三个整型的属性有效地区分可读数据和可写数据,使得读写之间相互没有冲突
·readerIndex(读指针):
指示读取的起始位置。每读取一个字节,readerIndex 自动增加1。一旦readerIndex与writerIndex相等,则表示ByteBuf不可读了。
·writerIndex(写指针):
指示写入的起始位置。每写一个字节,writerIndex自 动增加1。一旦增加到writerIndex与capacity()容量相等,则表示ByteBuf已经不可写 了。capacity()是一个成员方法,不是一个成员属性,它表示ByteBuf中可以写入的容 量。注意,它不是最大容量maxCapacity。
·maxCapacity(最大容量):
表示ByteBuf可以扩容的最大容量。当向ByteBuf写 数据的时候,如果容量不足,可以进行扩容。扩容的最大限度由maxCapacity的值来 设定,超过maxCapacity就会报错。

ByteBuf的重要方法:

第一组:容量系列

·capacity():表示ByteBuf的容量,它的值是以下三部分之和:废弃的字节数、可读字节数和可写字节数。
·maxCapacity():表示ByteBuf最大能够容纳的最大字节数。当向ByteBuf中写数据的时候,如果发现容量不足,则进行扩容,直到扩容到maxCapacity设定的上限。

第二组:写入系列

·isWritable():表示ByteBuf是否可写。如果capacity()容量大于writerIndex指针的位置,则表示可写,否则为不可写。 注意:如果isWritable()返回false,并不代表不 能再往ByteBuf中写数据了。如果Netty发现往ByteBuf中写数据写不进去的话,会自 动扩容ByteBuf
·writableBytes():取得可写入的字节数,它的值等于容量capacity()减去writerIndex。
·maxWritableBytes():取得最大的可写字节数,它的值等于最大容量maxCapacity减去writerIndex。
·writeBytes(byte[] src): 把src字节数组中的数据全部写到ByteBuf。这是最为常 用的一个方法
·writeTYPE(TYPE value):写入基础数据类型的数据。TYPE表示基础数据类型,包含了8大基础数据类型。具体如下:writeByte()、writeBoolean()、writeChar()、writeShort()、writeInt()、writeLong()、writeFloat()、writeDouble()。
·setTYPE(TYPE value):基础数据类型的设置,不改变writerIndex指针值,包含了8大基础数据类型的设置。具体如下:setByte()、setBoolean()、setChar()、setShort()、setInt()、setLong()、setFloat()、setDouble()。 setType系列与writeTYPE系 列的不同:setType系列不改变写指针writerIndex的值;writeTYPE系列会改变写指针 writerIndex的值
·markWriterIndex()与resetWriterIndex():这两个方法一起介绍。前一个方法表示把当前的写指针writerIndex属性的值保存在markedWriterIndex属性中;后一个方法表示把之前保存的markedWriterIndex的值恢复到写指针writerIndex属性中。markedWriterIndex属性相当于一个暂存属性,也定义在AbstractByteBuf抽象基类中。

第三组:读取系列

·isReadable( ):返回ByteBuf是否可读。如果writerIndex指针的值大于readerIndex指针的值,则表示可读,否则为不可读。
·readableBytes( ):返回表示ByteBuf当前可读取的字节数,它的值等于writerIndex减去readerIndex。
·readBytes(byte[] dst): 读取ByteBuf中的数据。将数据从ByteBuf读取到dst字节 数组中,这里dst字节数组的大小,通常等于readableBytes()。这个方法也是最为常 用的一个方法之一
·readType():读取基础数据类型,可以读取8大基础数据类型。具体如下:readByte()、readBoolean()、readChar()、readShort()、readInt()、readLong()、readFloat()、readDouble()。
·getTYPE(TYPE value): 读取基础数据类型,并且不改变指针值。具体如下:getByte()、getBoolean()、getChar()、getShort()、getInt()、getLong()、getFloat()、getDouble()。getType系列与readTYPE系列的不同:getType系列不会改变读指针readerIndex的值;readTYPE系列会改变读指针readerIndex的值。
·markReaderIndex( )与resetReaderIndex( ):这两个方法一起介绍。前一个方法表示把当前的读指针ReaderIndex保存在markedReaderIndex属性中。后一个方法表示把保存在markedReaderIndex属性的值恢复到读指针ReaderIndex中。markedReaderIndex属性定义在AbstractByteBuf抽象基类中。
这里用了默认的分配器,分配了一个初始容量为9,最大限制为100个字节的缓冲区:

【ByteBuf的引用计数】

Netty采用“计数器”来追踪ByteBuf的生命周期,一是对Pooled ByteBuf的支持,二是能够尽快地“发现”那些可以回收的ByteBuf(非Pooled),以便提升ByteBuf的分配和销毁的效率。
插个题外话: 什么是Pooled(池化)的ByteBuf缓冲区呢?在通信程序的执行过程中,Buffer缓冲区实例会被频繁创建、使用、释放。大家都知道,频繁创建对象、内存分配、释放内存,系统的开销大、性能低,如何提升性能、提高Buffer实例的使用率呢? 从Netty4版本开始,新增了对象池化的机制。即创建一个Buffer对象 池,将没有被引用的Buffer对象,放入对象缓存池中;当需要时,则重新从对象缓 存池中取出,而不需要重新创建
引用计数的大致规则如下:在 默认情况下,当创建完一个ByteBuf 时,它的引用为1;每次调用retain()方法,它的引用就加1;每次调用release()方法, 就是将引用计数减1;如果引用为0,再次访问这个ByteBuf对象,将会抛出异常;如 果引用为0,表示这个ByteBuf没有哪个进程引用它,它占用的内存需要回收。最后一次retain方法抛出了IllegalReferenceCountException异常。原因是:在此之前,缓冲区buffer的引用计数已经为0,不能再retain了。也就是说:在Netty中,引用计数为0的缓冲区不能再继续使用。
如果retain和release这两个方法,一次都不调用呢?则在缓冲区使用完成后,调用一次release,就是释放一次。 例如在Netty流水线上,中间所有的Handler业务处理 器处理完ByteBuf之后直接传递给下一个,由最后一个Handler负责调用release来释放 缓冲区的内存空间
当引用计数已经为0,Netty会进行ByteBuf的回收。分为两种情况:(1)Pooled池化的ByteBuf内存,回收方法是:放入可以重新分配的ByteBuf池子,等待下一次分配。(2)Unpooled未池化的ByteBuf缓冲区,回收分为两种情况:如果是堆(Heap)结构缓冲,会被JVM的垃圾回收机制回收;如果是Direct类型,调用本地方法释放外部内存(unsafe.freeMemory)。

【ByteBuf的Allocator分配器】

Netty通过ByteBufAllocator分配器来创建缓冲区和分配内存空间。Netty提供了ByteBufAllocator的两种实现:PoolByteBufAllocator和UnpooledByteBufAllocator。
1. PoolByteBufAllocator(池化ByteBuf分配器)将ByteBuf实例放入池中,提高了 性能,将内存碎片减少到最小;这个池化分配器采用了jemalloc高效内存分配的策 略,该策略被好几种现代操作系统所采用
2. UnpooledByteBufAllocator是普通的未池化ByteBuf分配器,它没有把ByteBuf放 入池中,每次被调用时,返回一个新的ByteBuf实例;通过Java的垃圾回收机制回
为了验证两者的性能,大家可以做一下对比试验,当然结论是使用池化分配器不依赖jvmGC:
(1)使用UnpooledByteBufAllocator的方式分配ByteBuf缓冲区,开启10000个长连接,每秒所有的连接发一条消息,再看看服务器的内存使用量的情况。实验的参考结果: 在短时间内,可以看到占到10GB多的内存空间,但随着系统 的运行,内存空间不断增长,直到整个系统内存被占满而导致内存溢出,最终系统 宕机
(2)把UnpooledByteBufAllocator换成PooledByteBufAllocator,再进行试验,看看服务器的内存使用量的情况。实验的参考结果: 内存使用量基本能维持在一个连接占用1MB左右的内存空 间,内存使用量保持在10GB左右,经过长时间的运行测试,我们会发现内存使用量 都能维持在这个数量附近,系统不会因为内存被耗尽而崩溃。在Netty中,默认的分配器为ByteBufAllocator.DEFAULT,可以通过Java系统参数(System Property)的选项io.netty.allocator.type进行配置,配置时使用字符串值:"unpooled","pooled"。

【ByteBuf缓冲区的类型】

根据内存的管理方不同,分为堆缓存区和直接缓存区,也就是Heap ByteBuf和Direct ByteBuf。另外,为了方便缓冲区进行组合,提供了一种组合缓存区
上面三种缓冲区的类型,无论哪一种,都可以通过池化(Pooled)、非池化 (Unpooled)两种分配器来创建和分配内存空间。下面对Direct Memory(直接内存)进行一下特别的介绍:
·Direct Memory不属于Java堆内存,所分配的内存其实是调用操作系统malloc()函数来获得的;由Netty的本地内存堆Native堆进行管理。
·Direct Memory容量可通过-XX:MaxDirectMemorySize来指定,如果不指定,则默认与Java堆的最大值(-Xmx指定)一样。注意:并不是强制要求,有的JVM默认Direct Memory与-Xmx无直接关系。
· Direct Memory的使用避免了Java堆和Native堆之间来回复制数据。在某些应用 场景中提高了性能
· 在需要频繁创建缓冲区的场合,由于创建和销毁Direct Buffer(直接缓冲区) 的代价比较高昂,因此不宜使用Direct Buffer。也就是说,Direct Buffer尽量在池化 分配器中分配和回收。如果能将Direct Buffer进行复用,在读写频繁的情况下,就可 以大幅度改善性能。
· 对Direct Buffer的读写比Heap Buffer快,但是它的创建和销毁比普通Heap Buffer慢。·在Java的垃圾回收机制回收Java堆时,Netty框架也会释放不再使用的DirectBuffer缓冲区,因为它的内存为堆外内存,所以清理的工作不会为Java虚拟机(JVM)带来压力。注意一下垃圾回收的应用场景:(1)垃圾回收仅在Java堆被填满,以至于无法为新的堆分配请求提供服务时发生;(2)在Java应用程序中调用System.gc()函数来释放内存。

【三类ByteBuf使用对比】

·创建的方法不同:Heap ByteBuf通过调用分配器的buffer()方法来创建;而Direct ByteBuf的创建,是通过调用分配器的directBuffer()方法。
·Heap ByteBuf缓冲区可以直接通过array()方法读取内部数组;而Direct ByteBuf缓冲区不能读取内部数组。
·可以调用hasArray()方法来判断是否为Heap ByteBuf类型的缓冲区;如果hasArray()返回值为true,则表示是Heap堆缓冲,否则就不是。
·Direct ByteBuf要读取缓冲数据进行业务处理,相对比较麻烦,需要通过getBytes/readBytes等方法先将数据复制到Java的堆内存,然后进行其他的计算。
注意,如果hasArray()返回false,不一定代表缓冲区一定就是Direct ByteBuf直接缓冲区,也有可能是CompositeByteBuf缓冲区。在很多通信编程场景下,需要多个ByteBuf组成一个完整的消息:例如HTTP协议传输时消息总是由Header(消息头)和Body(消息体)组成的。如果传输的内容很长,就会分成多个消息包进行发送,消息中的Header就需要重用,而不是每次发送都创建新的Header。这是就是使用CompositeByteBuf较多:
在以上代码中,使用到了Netty中一个非常方便的类——Unpooled帮助类,用它来创建和使用非池化的缓冲区。另外,还可以在Netty程序之外独立使用Unpooled帮助类。 另外,从Netty 4.1开始ByteBuf的默认类型是Direct ByteBuf直接内存。大家知 道,Java不能直接访问Direct ByteBuf内部的数据,必须先通过getBytes、readBytes等 方法,将数据读入Java数组中,然后才能继续在数组中进行处理

【ByteBuf浅层复制】

ByteBuf的浅层复制分为两种,有切片(slice)浅层复制和整体(duplicate)浅层复制。首先说明一下,浅层复制是一种非常重要的操作。可以很大程度地避免内存复制。这一点对于大规模消息通信来说是非常重要的。
1.slice切片浅层复制
ByteBuf的slice方法可以获取到一个ByteBuf的一个切片。一个ByteBuf可以进行多次的切片浅层复制;多次切片后的ByteBuf对象可以共享一个存储区域。
2.duplicate整体浅层复制
和slice切片不同,duplicate()返回的是源ByteBuf的整个对象的一个浅层复制,包括如下内容:
·duplicate的读写指针、最大容量值,与源ByteBuf的读写指针相同。
·duplicate()不会改变源ByteBuf的引用计数。
·duplicate()不会复制源ByteBuf的底层数据。
duplicate()和slice()方法都是浅层复制。不同的是,slice()方法是切取一段的浅层复制,而duplicate( )是整体的浅层复制。
浅层复制方法不会实际去复制数据,也不会改变ByteBuf的引用计数,这就会导致一个问题:在源ByteBuf调用release()之后,一旦引用计数为零,就变得不能访问了;在这种场景下,源ByteBuf的所有浅层复制实例也不能进行读写了;如果强行对浅层复制实例进行读写,则会报错。因此,在调用浅层复制实例时,可以通过调用一次retain()方法来增加引用,表示它们对应的底层内存多了一次引用,引用计数为2。在浅层复制实例用完后,需要调用两次release()方法,将引用计数减一,这样就不影响源ByteBuf的内存释放。

【ByteBuf在netty中的使用】

1.netty入站ByteBuf的创建:
在入站处理时,Netty是何时自动创建入站的ByteBuf的呢? 查看Netty源代码,我们可以看到,Netty的Reactor反应器线程会在底层的Java NIO通道读数据时,也就是AbstractNioByteChannel.NioByteUnsafe.read()处,调用 ByteBufAllocator方法,创建ByteBuf实例,从操作系统缓冲区把数据读取到Bytebuf 实例中,然后调用pipeline.fireChannelRead(byteBuf)方法将读取到的数据包送入到入 站处理流水线中
2.netty入站ByteBuf的释放:
方式一:TailHandler自动释放 Netty默认会在ChannelPipline通道流水线的最后添加一个TailHandler末尾处理 器,它实现了默认的处理方法,在这些方法中会帮助完成ByteBuf内存释放的工作。 在默认情况下,如果每个InboundHandler入站处理器,把最初的ByteBuf数据包 一路往下传,那么TailHandler末尾处理器会自动释放掉入站的ByteBuf实例
方式二:SimpleChannelInboundHandler自动释放 如果Handler业务处理器需要截断流水线的处理流程,不将ByteBuf数据包送入 后边的InboundHandler入站处理器,这时,流水线末端的TailHandler末尾处理器自动 释放缓冲区的工作自然就失效了。在这种场景下,Handler业务处理器有两种选择:
·手动释放ByteBuf实例。
· 继承SimpleChannelInboundHandler,利用它的自动释放功能
SimpleChannelInboundHandler自动释放源代码:
3.netty出站ByteBuf的释放:
出站缓冲区的自动释放方式: HeadHandler自动释放。在出站处理流程中,申请分配到的ByteBuf主要是通过HeadHandler完成自动释放的。 出站处理用到的Bytebuf缓冲区,一般是要发送的消息,通常由Handler业务处理 器所申请而分配的。例如,在write出站写入通道时,通过调用 ctx.writeAndFlush(Bytebufmsg),Bytebuf缓冲区进入出站处理的流水线。在每一个出 站Handler业务处理器中的处理完成后,最后数据包(或消息)会来到出站的最后一 棒HeadHandler,在数据输出完成后,Bytebuf会被释放一次,如果计数器为零,将 被彻底释放掉
4.ByteBuf的传递:
channelRead方法的msg参数的形参类型不是ByteBuf,而是Object,为什么呢?实际上,msg的形参类型是由流水线的上一站决定的。 大家知道,入站处理的流程是:Netty读取底层的二进制数据,填充到 msg时,msg是ByteBuf类型,然后经过流水线,传入到第一个入站处理器;每一个 节点处理完后,将自己的处理结果(类型不一定是ByteBuf)作为msg参数,不断向 后传递。因此,msg参数的形参类型,必须是Object类型。不过,可以肯定的是, 一个入站处理器的channelRead方法的msg实参类型,绝对是ByteBuf类型,因为它是 Netty读取到的ByteBuf数据包 ,所以可以强 制转成ByteBuf类型

【ByteBuf的入站解码】

什么叫作Netty的解码器呢?首先,它是一个InBound入站处理器,解码器负责处理“入站数据”。Netty内置了这个解码器,叫作 ByteToMessageDecoder,位在Netty的io.netty.handler.codec包中。 强调一下,所有的Netty中的解码器,都是Inbound入站处理器类型,都直接或 者间接地实现了ChannelInboundHandler接口
ByteToMessageDecoder是一个非常重要的解码器基类,它是一个抽象类,实现了解码的基础逻辑和流程。 ByteToMessageDecoder继承自 ChannelInboundHandlerAdapter适配器,是一个入站处理器,实现了从ByteBuf到Java POJO对象的解码功能。查看Netty源代码,我们会惊奇地发现:ByteToMessageDecoder仅仅是个抽象类,不能以实例化方式创建对象。也就是说,直接通过ByteToMessageDecoder类,并不能完成Bytebuf字节码到具体Java类型的解码,还得依赖于它的具体实现。ByteToMessageDecoder的解码方法名为decode。通过源代码我们可以发现,decode方法只是提供了一个抽象方法,也就是说,decode方法中的具体解码过程,ByteToMessageDecoder没有具体的实现。换句话说,如何将Bytebuf数据变成Object数据,需要子类去完成,父类不管。总之,作为解码器的父类,ByteToMessageDecoder仅仅提供了一个流程性质的框架:它仅仅将子类的decode方法解码之后的Object结果,放入自己内部的结果列表List<Object>中,最终,父类会负责将List<Object>中的元素,一个一个地传递给下一个站。哦,不对!父类还没有那么“勤快”,而是将子类的Object结果放入父类的List<Object>列表,也是交由子类的decode方法完成的。
实现一个ByteBuf的整数解码器:
但是如何解决分包问题呢:
前面讲到,底层通信协议是分包传输的,一份数据可能分几次达到对端。发送端出去的包在传输过程中会进行多次的拆分和组装。接收端所收到的包和发送端所发送的包不是一模一样的,具体如图7-2所示:在发送端发出4个字符串,Netty NIO接收端可能只是接收到了3个ByteBuf数据缓冲。在Java OIO流式传输中,不会出现这样的问题,因为它的策略是:不读到完整的信息,就一直阻塞程序,不向后执行。但是,在Java的NIO中,由于NIO的非阻塞性,就会出现图7-2这样的问题。怎样保证一次性读取到完整的数据,就成了一个大问题
我们知道,Netty接收到的数据都可以通过解码器进行解码。那么,Netty通过什么样的解码器对图7-2中的3个ByteBuf数据缓冲数据进行解码,而后得到和发送端一模一样的4个字符串,是怎么办到的呢?对于上面这个问题,还是可以使用ReplayingDecoder来解决。前文讲到,在进行数据解析时,如果发现当前ByteBuf中所有可读的数据不够,ReplayingDecoder会结束解析,直到可读数据是足够的。这一切都是在ReplayingDecoder内部进行,它是通过和缓冲区装饰器类ReplayingDecoderBuffer相互配合完成的,根本就不需要用户程序来操心。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

消息 ByteBuf 详解 的相关文章

  • Google 地图查询返回的 JSON 包含像 \x26 这样的编码字符(如何解码?)

    在 Java 应用程序中 我获取 JSON 来自 Google 地图 其中包含以下字符 x26我想将其转换为其原始字符 据我所知 这是一个 UTF 8 表示法 但我不完全确定 在源 JSON 中 可能会出现各种编码字符 例如 x3c div
  • 如何打印整个字符串池?

    我想打印包含文字的整个字符串池String使用添加的对象intern 就在垃圾收集之前 JDK有没有隐式的方法来进行这样的操作 我们如何检查字符串池 EDIT The comment suggests that there may be a
  • 通过 InjectMocks Spy 注入对象

    我需要对一个类运行一系列单元测试 该类具有 Autowired Logger 实现 实现的基本思想是 Mock Logger logger InjectMocks TestedClass tested 但我想保存日志输出功能 Mockito
  • 有没有好的方法来解析用户代理字符串?

    我有一个Java接收模块User Agent来自最终用户浏览器的字符串的行为需要略有不同 具体取决于浏览器类型 浏览器版本甚至操作系统 例如 FireFox 7 0 Win7 Safari 3 2 iOS9 我明白了User Agent由于
  • MI设备中即使应用程序被杀死,如何运行后台服务

    您好 我正在使用 alaram 管理器运行后台服务 它工作正常 但对于某些 mi 设备 后台服务无法工作 我使用了服务 但它无法工作 如何在 mi 中运行我的后台服务 MI UI有自己的安全选项 所以你需要的不仅仅是上面提到的粘性服务 你需
  • PropertySources 中各种源的优先级

    Spring引入了新的注释 PropertySources对于所有标记为的类 Configuration since 4 0 需要不同的 PropertySource作为论证 PropertySources PropertySource c
  • Java 变量的作用域

    我不明白为什么这段代码的输出是10 package uno public class A int x 10 A int x 12 new B public static void main String args int x 11 new
  • 使用 java 按电子邮件发送日历邀请

    我正在尝试使用 java 发送每封电子邮件的日历邀请 收件人收到电子邮件 但不会显示接受或拒绝的邀请 而是将该事件自动添加到他的日历中 我正在使用 ical4j jar 构建活动 邀请 private Calendar getInvite
  • 想要开发像 Facebook 这样的网站 - 处理数百万个请求 - 高性能 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我想用 Java 开发一个像 Fac
  • 使用 Guava 联合两个 ImmutableEnumSets

    我想联合两个ImmutableEnumSets来自番石榴 这是我的尝试 public final class OurColors public enum Colors RED GREEN BLUE YELLOW PINK BLACK pub
  • 具有多种值类型的 Java 枚举

    基本上我所做的是为国家编写一个枚举 我希望不仅能够像国家一样访问它们 而且还能够访问它们的缩写以及它们是否是原始殖民地 public enum States MASSACHUSETTS Massachusetts MA true MICHI
  • 覆盖 MATLAB 默认静态 javaclasspath 的最佳方法

    MATLAB 配置为在搜索用户可修改的动态路径之前搜索其静态 java 类路径 不幸的是 静态路径包含相当多非常旧的公共库 因此如果您尝试使用新版本 您可能最终会加载错误的实现并出现错误 例如 静态路径包含 google collectio
  • 从 html 页面和 javascript 调用 java webservice

    我正在尝试从 javascript 调用 java 实现的 Web 服务 使用 NetBeans IDE 我读过很多关于 jQuery 和 AJAX 的内容 但我似乎无法掌握它 假设我的 Web 服务 WSDL 位于 http localh
  • jmap - 组织和堆操作会给 jvm 带来开销吗?

    正如标题所述 需要多少开销jmap histo and jmap heap分别带到jvm 如果一个内存敏感的 Java 进程处于OutOfMemory 例如 大约 96 的堆已满 并且无法通过 full gc 清除 其中一项操作是否有可能将
  • 如何使用 Mockito 和 Junit 模拟 ZonedDateTime

    我需要模拟一个ZonedDateTime ofInstant 方法 我知道SO中有很多建议 但对于我的具体问题 到目前为止我还没有找到任何简单的解决办法 这是我的代码 public ZonedDateTime myMethodToTest
  • ExceptionHandler 不适用于 Throwable

    我们的应用程序是基于 Spring MVC 的 REST 应用程序 我正在尝试使用 ExceptionHandler 注释来处理所有错误和异常 I have ExceptionHandler Throwable class public R
  • 我想要一个 Java 阿拉伯语词干分析器

    我正在寻找阿拉伯语的 Java 词干分析器 我找到了一个名为 AraMorph 的库 但它的输出是无法控制的 并且它会形成不需要的单词 还有其他阿拉伯语词干分析器吗 这是新的阿拉伯语词干分析器 Assem 的阿拉伯语轻词干分析器 http
  • Java中获取集合的幂集

    的幂集为 1 2 3 is 2 3 2 3 1 2 1 3 1 2 3 1 假设我有一个Set在爪哇中 Set
  • Spring Boot MSSQL Kerberos 身份验证

    目前在我的春季靴子中application properties文件中 我指定以下行来连接到 MSSql 服务器 spring datasource url jdbc sqlserver localhost databaseName spr
  • java中如何找到class文件的包

    我正在编写一个使用 class 文件的 java 程序 我希望能够读取文件系统上的 class 文件 使用 InputStream 并确定它所在的包 该 class 文件可能不在一个好的包目录结构中 它可能位于某个随机位置 我怎样才能做到这

随机推荐

  • ECharts合并地图上的区域

    对于某些特定需求 官方下载的地图数据可能并不能完全满足 例如 要求显示中国地图 但需要将山东江苏和浙江这3个省合并起来 显示 东部区域 其他省份不变 于是就需要对官方提供的地图数据进行修改 一个思路是借助第三方工具 生成新区域的轮廓点 然后
  • 刷脸支付打造数字小镇应用的全新探索

    刷脸支付打造数字小镇是特色小镇数字化应用的一项全新探索 主要是依托一部手机游云南平台 在精准治理 惠民服务 生态宜居等领域进行数字化应用 丽江大研古城数字小镇启动建设以来已经取得初步成效 1月16日 从云南省发展和改革委员会举行的1月定时定
  • 28、宏任务与微任务

    原理图 setImmediate 也是宏任务 在 Node 环境下 微任务还有 process nextTick JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队 宏列队 用来保存待执行的宏任务 回调 比如 定时器回调 DOM
  • VMware Workstation 15虚拟机使用教程

    VMware Workstation 15虚拟机使用教程 前言 一 在虚拟机中安装win7 1 1新建虚拟机一个win7虚拟机 1 2插入虚拟机光盘 即指定ISO镜像文件 1 3设置虚拟机的BIOS 光驱为第一启动 1 4开始在虚拟机中安装
  • 数据库中的字段名与实体类中的属性名不能一一对应时的三种处理方式

    当查询结果的列名和java对象的属性名对应不上时需要采用下列方式进行处理 第一种方式 在查询语句中使用关键字 as 给列起别名 第二种方式 使用resultMap结果映射 第三种方式 开启驼峰命名自动映射 配置settings 实体类Car
  • Douglas-Peucker算法的Matlab实现

    代码 Douglas Peucker 道格拉斯 普克算法 function curve dpEdgeContour pnts clc clear A readmatrix EdgeContour1 xls Sheet 1 输入数据 x A
  • ES6系列教程第二篇--Iterator 详解

    一 什么是for of循环 对于如下一个数组 遍历其中的值方法有哪些 var arr a b c 首先想到的可能就是如下这种 这也是js最原始的遍历方法 和java的语法一样 var arr a b c for var i 0 i
  • 手写代码-Hudi-Demo

    import org apache hudi config HoodieIndexConfig import org apache hudi index HoodieIndex import org apache hudi DataSour
  • 托马斯推荐-android源码网站

    2019独角兽企业重金招聘Python工程师标准 gt gt gt http grepcode com 转载于 https my oschina net u 3407708 blog 1587258
  • js的简单数组去重

    介绍几种数组去重的方法 举例数组 const arr 1 1 2 2 null null undefined undefined new String 1 new String 1 a a NaN NaN set function uniq
  • 轮胎企业RFID生产线管理(MES系统)应用

    1 项目背景 在轮胎生产制造企业中 轮胎生产信息的正确采集和存储将对控制轮胎的生产过程 质量检验和质量跟踪等方面起着重要作用 目前 企业MES系统依靠手工记录和条码扫描的方式进行数据采集 由于轮胎生产工序多且复杂 半成品物料就有十几种工序
  • Spring整合Hibernate实现Spring Data JPA

    在上一篇文章 Spring整合Hibernate实现JPA持久化 中 我们已经介绍了怎样在Spring容器中合理地集成Hibernate来实现JPA的ORM机制 但是 细心的读者会发现 上一篇文章中使用了EntityManager来直接与数
  • UE4 更改DerivedDataCache 位置

    Epic Games UE 4 24 Engine Config BaseEngine ini InstalledDerivedDataBackendGraph MinimumDaysToKeepFile 7 Root Type KeyLe
  • OpenCV与Eigen (C++)【学习备忘】

    可点击OpenCV来自简书 OpenCV官网 OpenCV中的单位 棋盘格边长尺寸 mm 机器视觉 OPENCV 一 各个模块简介 二 数据类型与类对象 1 数据类型 点类 size类 向量类 矩形类 指针类 异常类等 2 类对象 2 1
  • maven资源

    http blog csdn net happyteafriends article details 7449642 jsp转换为静态页面参考 http blog csdn net zklxuankai article details 81
  • 最新Anaconda保姆级安装教程:手把手带你走进数据分析Anaconda安装门槛

    Python数据分析入门 基础概念和最新Anaconda安装 综述 什么是数据分析 数据分析是用适当的方法对收集来的大量数据进行分析 帮助人们作出判断 以便采取适当行动 mermaid svg lsc3TUoKjNk4ccj7 font f
  • 【Linux】—— vim常用操作命令

    这里写目录标题 1 vim的基本概念 2 命令模式的操作 光标跳转 剪贴复制 撤销修改 3 底行模式操作 4 配置vim编辑器 使用文件配置 快速配置 1 vim的基本概念 概念 vim重点解决代码编写的问题 本质文本编辑器 是具有多模式的
  • 简单搭建frp服务(服务端与客户端)

    记录搭建frp服务的过程 首先下载frp 本人使用的是0 22 0版本 其它版本可能不适用 可以通过wget命令进行下载 wget https github com fatedier frp releases download v0 22
  • ElacsticSearch中集合了分组、排序,求和,group by sort sum sum(A*B)

    最近做了一个项目 以前是mysql中 现在使用es进行查询 mysql的查询是 返回的结果是 1 字段求和的数据 2 字段相乘再求和在除数据 这个问题卡了一天 3 根据某个字段分组 4 根据某个字段排序 根据思路因为是求和 所以需要用到聚合
  • 消息 ByteBuf 详解

    Netty提供了ByteBuf来替代Java NIO的ByteBuffer缓冲区 以操纵内存缓冲区 与Java NIO的ByteBuffer相比 ByteBuf的优势如下 Pooling 池化 这点减少了内存复制和GC 提升了效率 复合缓冲