Spring 5 中文解析之核心篇-Null-safety和DataBuffer

技术交流群:

Spring 5 中文解析之核心篇-Null-safety和DataBuffer_第1张图片

Null-safety

尽管Java不允许你使用其类型系统来表示空安全性,但Spring框架现在在org.springframework.lang包中提供了以下注解,以使你声明API和字段的空能力:

  • @Nullable:指示特定参数,返回值或字段可以为null的注解。
  • @NonNull: 指示特定参数,返回值或字段不能为null的注释(分别适用于@NonNullApi@NonNullFields的参数/返回值和字段不需要)。
  • @NonNullApi:包级别的注解,它声明非null作为参数和返回值的默认语义。
  • @NonNullFields: 程序包级别的注释,它声明非null为字段的默认语义。

Spring框架本身利用了这些注解,但是它们也可以在任何基于Spring的Java项目去使用,以声明null安全的API和可选的null安全的字段。尚不支持泛型类型参数,varargs和数组元素的可空性,但应在即将发布的版本中使用它们,有关最新信息,请参见SPR-15942。可空性声明预计将在Spring Framework版本之间进行微调,包括次要版本。在方法主体内部使用的类型的可空性超出了此功能的范围。

其他常见的库(如Reactor和Spring Data)提供了使用类似可空性设置的空安全API,从而为Spring应用程序开发人员提供了一致的总体体验。

7.1 使用场景

除了为Spring Framework API可空性提供显式声明外,IDE(例如IDEA或Eclipse)还可以使用这些注解提供与空安全有关的有用警告,以避免在运行时出现NullPointerException

由于Kotlin原生支持null安全,因此它们还用于在Kotlin项目中使Spring API为null安全。Kotlin支持文档中提供了更多详细信息。

7.2 JSR-305 元数据注解

Spring注解使用JSR 305注解(广泛传播的JSR)进行元注释。JSR-305元注释使工具供应商(如IDEA或Kotlin)以通用方式提供了空安全支持,而无需对Spring注释进行硬编码支持。

既不需要也不建议向项目类路径添加JSR-305依赖项以利用Spring空安全API。只有诸如在其代码库中使用空安全注释的基于Spring的库之类的项目才应添加com.google.code.findbugs:jsr305:3.0.2compileOnly Gradle配置或Maven提供的范围,以避免编译警

Data Buffers and Codecs

Java NIO提供了ByteBuffer,但是许多库在顶部构建了自己的字节缓冲区API,特别是对于网络操作,其中重用缓冲区和/或使用直接缓冲区对性能有利。例如,Netty具有ByteBuf层次结构,Undertow使用XNIO,Jetty使用具有要释放的回调的池化字节缓冲区,依此类推。spring-core模块提供了一组抽象,可以与各种字节缓冲区API配合使用,如下所示:

  • DataBufferFactory抽象数据缓冲区的创建。
  • DataBuffer表示一个字节缓冲区,可以池化。
  • DataBufferUtils提供数据缓冲区的实用方法。
  • Codecs 将流数据缓冲区流解码或编码为更高级别的对象。
8.1 DataBufferFactory

DataBufferFactory用于通过以下两种方式之一创建数据缓冲区:

  1. 分配一个新的数据缓冲区,可以选择预先指定容量(如果已知),即使DataBuffer的实现可以按需增长和缩小,该容量也会更有效。
  2. 包装一个现有的byte []java.nio.ByteBuffer,它们用DataBuffer实现装饰给定的数据,并且不涉及分配。

请注意,WebFlux应用程序不会直接创建DataBufferFactory,而是通过客户端的ServerHttpResponseClientHttpRequest访问它。工厂的类型取决于基础客户端或服务器,例如NettyDataBufferFactory用于Reactor Netty,DefaultDataBufferFactory用于其他。

8.2 DataBuffer

DataBuffer接口提供与java.nio.ByteBuffer类似的操作,但还带来了一些其他好处,其中一些是受Netty ByteBuf启发的。以下是部分优点清单:

  • 具有独立位置的读取和写入,即不需要调用flip()在读取和写入之间交替。
  • java.lang.StringBuilder一样,容量可以按需扩展。
  • 通过PooledDataBuffer进行缓冲池和引用计数。
  • 将缓冲区查看为java.nio.ByteBufferInputStream或OutputStream。
  • 确定给定字节的索引或最后一个索引。
8.3 PooledDataBuffer

如Javadoc中针对ByteBuffer所述,字节缓冲区可以是直接的也可以是非直接的。直接缓冲区可以驻留在Java堆之外,从而无需复制本机I / O操作。这使得直接缓冲区对于通过套接字接收和发送数据特别有用,但直接创建和释放它们的成本也更高,这导致了缓冲池的想法。

PooledDataBufferDataBuffer的扩展,可帮助进行引用计数,这对于字节缓冲区池至关重要。它是如何工作的?分配PooledDataBuffer时,引用计数1。调用keep()会增加计数,而调用release()会减少计数。只要计数大于0,就保证不会释放缓冲区。当计数减少到0时,可以释放池中的缓冲区,这实际上意味着将为缓冲区保持的内存返回到内存池。

请注意,与其直接在PooledDataBuffer上进行操作,不如在大多数情况下,最好使用DataBufferUtils中的便捷方法,仅当它是PooledDataBuffer的实例时才将释放或保留应用于DataBuffer

8.4 DataBufferUtils

DataBufferUtils提供了许多实用程序方法来对数据缓冲区进行操作:

  • 将数据缓冲区流连接到单个缓冲区中,可能具有零个副本,例如通过复合缓冲区(如果基础字节缓冲区API支持的话)。
  • InputStreamNIO通道转换为Flux ,反之亦然,将Publisher 转换为OutputStreamNIO通道。
  • 如果缓冲区是PooledDataBuffer的实例,则释放或保留DataBuffer的方法。
  • 从字节流中跳过或获取,直到特定的字节数为止。
8.5 编解码器

org.springframework.core.codec包提供以下策略接口:

  • 编码器,用于将Publisher 编码为数据缓冲区流。
  • 解码器,用于将Publisher 解码为更高级别的对象流。

spring-core模块提供byte []ByteBuffer、DataBuffer、ResourceString编码器和解码器实现。spring-web模块添加了Jackson JSON、Jackson Smile、JAXB2、

Protocol Buffers和其他编码器和解码器。请参阅WebFlux部分中的编解码器。

8.6 使用DataBuffer

使用数据缓冲区时,必须特别小心以确保释放缓冲区,因为它们可能会被池化。我们将使用编解码器来说明其工作原理,但是这些概念会更普遍地应用。让我们看看编解码器在内部必须执行哪些操作来管理数据缓冲区。

在创建更高级别的对象之前,解码器是最后一个读取输入数据缓冲区的对象,因此,它必须按以下方式释放它们:

  1. 如果解码器只是读取每个输入缓冲区并准备立即释放它,则可以通过DataBufferUtils.release(dataBuffer)这样做。
  2. 如果Decoder使用的是Flux或Mono运算符(例如flatMapreduce和其他在内部预取和缓存数据项的运算符),或者正在使用诸如filterskip的运算符以及其他遗漏项的运算符,则doOnDiscard(PooledDataBuffer.class,DataBufferUtils:: release)必须添加到组合链中,以确保在丢弃此类缓冲区之前将其释放,这也可能是错误或取消信号的结果。
  3. 如果解码器以任何其他方式保留一个或多个数据缓冲区,则它必须确保在全读取时释放它们,或者在读取和释放缓存的数据缓冲区之前发生错误或取消信号的情况下。

请注意,DataBufferUtils#join提供了一种安全有效的方法来将数据缓冲区流聚合到单个数据缓冲区中。同样,skipUntilByteCounttakeUntilByteCount是供解码器使用的其他安全方法。

编码器分配其他人必须读取(和释放)的数据缓冲区。因此,编码器无事可做。但是,如果在使用数据填充缓冲区时发生序列化错误,则编码器必须小心释放数据缓冲区。例如:

DataBuffer buffer = factory.allocateBuffer();
boolean release = true;
try {
    // serialize and populate buffer..
    release = false;
}
finally {
    if (release) {
        DataBufferUtils.release(buffer);
    }
}
return buffer;

编码器的使用者负责释放其接收的数据缓冲区。在WebFlux应用程序中,编码器的输出用于写入HTTP服务器响应或客户端HTTP请求,在这种情况下,释放数据缓冲区是代码写入服务器响应或客户端的责任。

请注意,在Netty上运行时,有用于调试缓冲区泄漏的调试选项。

参考代码:com.liyong.ioccontainer.service.buffer.SpringByteBuffer

作者

个人从事金融行业,就职过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就职于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。

博客地址:http://youngitman.tech

CSDN:https://blog.csdn.net/liyong1028826685

微信公众号:

Spring 5 中文解析之核心篇-Null-safety和DataBuffer_第2张图片

你可能感兴趣的:(Spring5全栈知识体系,java,spring)