尽管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应用程序开发人员提供了一致的总体体验。
除了为Spring Framework API可空性提供显式声明外,IDE(例如IDEA或Eclipse)还可以使用这些注解提供与空安全有关的有用警告,以避免在运行时出现NullPointerException
。
由于Kotlin原生支持null安全,因此它们还用于在Kotlin项目中使Spring API为null
安全。Kotlin支持文档中提供了更多详细信息。
Spring注解使用JSR 305注解(广泛传播的JSR)进行元注释。JSR-305元注释使工具供应商(如IDEA或Kotlin)以通用方式提供了空安全支持,而无需对Spring注释进行硬编码支持。
既不需要也不建议向项目类路径添加JSR-305依赖项以利用Spring空安全API。只有诸如在其代码库中使用空安全注释的基于Spring的库之类的项目才应添加com.google.code.findbugs:jsr305:3.0.2
的compileOnly
Gradle配置或Maven提供的范围,以避免编译警
Java NIO提供了ByteBuffer
,但是许多库在顶部构建了自己的字节缓冲区API,特别是对于网络操作,其中重用缓冲区和/或使用直接缓冲区对性能有利。例如,Netty具有ByteBuf
层次结构,Undertow使用XNIO
,Jetty使用具有要释放的回调的池化字节缓冲区,依此类推。spring-core
模块提供了一组抽象,可以与各种字节缓冲区API配合使用,如下所示:
DataBufferFactory
抽象数据缓冲区的创建。DataBuffer
表示一个字节缓冲区,可以池化。DataBufferUtils
提供数据缓冲区的实用方法。DataBufferFactory
DataBufferFactory
用于通过以下两种方式之一创建数据缓冲区:
DataBuffer
的实现可以按需增长和缩小,该容量也会更有效。byte []
或java.nio.ByteBuffer
,它们用DataBuffer
实现装饰给定的数据,并且不涉及分配。请注意,WebFlux应用程序不会直接创建DataBufferFactory
,而是通过客户端的ServerHttpResponse
或ClientHttpRequest
访问它。工厂的类型取决于基础客户端或服务器,例如NettyDataBufferFactory
用于Reactor Netty,DefaultDataBufferFactory
用于其他。
DataBuffer
DataBuffer
接口提供与java.nio.ByteBuffer
类似的操作,但还带来了一些其他好处,其中一些是受Netty ByteBuf启发的。以下是部分优点清单:
flip()
在读取和写入之间交替。java.lang.StringBuilder
一样,容量可以按需扩展。PooledDataBuffer
进行缓冲池和引用计数。java.nio.ByteBuffer
、InputStream
或OutputStream。PooledDataBuffer
如Javadoc中针对ByteBuffer所述,字节缓冲区可以是直接的也可以是非直接的。直接缓冲区可以驻留在Java堆之外,从而无需复制本机I / O操作。这使得直接缓冲区对于通过套接字接收和发送数据特别有用,但直接创建和释放它们的成本也更高,这导致了缓冲池的想法。
PooledDataBuffer
是DataBuffer
的扩展,可帮助进行引用计数,这对于字节缓冲区池至关重要。它是如何工作的?分配PooledDataBuffer
时,引用计数1。调用keep()
会增加计数,而调用release()
会减少计数。只要计数大于0,就保证不会释放缓冲区。当计数减少到0时,可以释放池中的缓冲区,这实际上意味着将为缓冲区保持的内存返回到内存池。
请注意,与其直接在PooledDataBuffer
上进行操作,不如在大多数情况下,最好使用DataBufferUtils
中的便捷方法,仅当它是PooledDataBuffer
的实例时才将释放或保留应用于DataBuffer
。
DataBufferUtils
DataBufferUtils
提供了许多实用程序方法来对数据缓冲区进行操作:
InputStream
或NIO
通道转换为Flux ,反之亦然,将Publisher 转换为OutputStream
或NIO
通道。PooledDataBuffer
的实例,则释放或保留DataBuffer
的方法。org.springframework.core.codec
包提供以下策略接口:
spring-core
模块提供byte []
、ByteBuffer
、DataBuffer、Resource
和String
编码器和解码器实现。spring-web
模块添加了Jackson JSON、Jackson Smile、JAXB2、
Protocol Buffers和其他编码器和解码器。请参阅WebFlux部分中的编解码器。
DataBuffer
使用数据缓冲区时,必须特别小心以确保释放缓冲区,因为它们可能会被池化。我们将使用编解码器来说明其工作原理,但是这些概念会更普遍地应用。让我们看看编解码器在内部必须执行哪些操作来管理数据缓冲区。
在创建更高级别的对象之前,解码器是最后一个读取输入数据缓冲区的对象,因此,它必须按以下方式释放它们:
DataBufferUtils.release(dataBuffer)
这样做。Decoder
使用的是Flux
或Mono运算符(例如flatMap
,reduce
和其他在内部预取和缓存数据项的运算符),或者正在使用诸如filter
,skip
的运算符以及其他遗漏项的运算符,则doOnDiscard(PooledDataBuffer.class,DataBufferUtils:: release)
必须添加到组合链中,以确保在丢弃此类缓冲区之前将其释放,这也可能是错误或取消信号的结果。请注意,DataBufferUtils#join
提供了一种安全有效的方法来将数据缓冲区流聚合到单个数据缓冲区中。同样,skipUntilByteCount
和takeUntilByteCount
是供解码器使用的其他安全方法。
编码器分配其他人必须读取(和释放)的数据缓冲区。因此,编码器无事可做。但是,如果在使用数据填充缓冲区时发生序列化错误,则编码器必须小心释放数据缓冲区。例如:
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
微信公众号: