仅接受写入 ByteArrayOutputStream 对象的部分消息

Accept only part of message written to ByteArrayOutputStream object

我正在尝试获取 SOAPMessage 对象的原始 xml,我正在使用 ByteArrayOutputStream class 进行此操作,但是一旦将压缩附件添加到消息对象,它就会失去可读性。

这是我的代码:

ByteArrayOutputStream out = new ByteArrayOutputStream();
message.writeTo(out);
strMsg = new String(out.toByteArray());

其中消息是 SOAPMessage 对象。 问题是,当我将它打印到标准输出或日志或其他一些输出时,strMsg 可能很大并且包含压缩附件的字节数据。有没有办法在将消息写入 ByteArrayOutputStream 对象时截断它?我知道,我可以轻松截断 strMsg,但我看不出有什么理由在 ByteArrayOutputStream 中收集所有数据。

I know, that I can easily truncate strMsg, but I don't see a reason to collect all the data inside ByteArrayOutputStream.

问题是您只有 writeTo 方法可以使用,并且该方法旨在输出整个消息。

您可以创建 ByteArrayOutputStream 的自定义变体,它只接受指定数量的字节。当 if writeTo 超过限制时,你的 class 需要抛出 IO 异常。

实际问题是创建和抛出异常的代价很高。这才是真言真言"Only use exceptions in exceptional case"-https://softwareengineering.stackexchange.com/questions/184654/ive-been-told-that-exceptions-should-only-be-used-in-exceptional-cases-how-do的基础。如果您正在记录大量 SOAP 消息,那可能是个问题。

异常创建/抛出的主要性能瓶颈实际上是异常对象的创建:特别是 Throwable 构造函数调用 fillInStacktrace 捕获堆栈帧的部分。有几种方法可以改善这一点:

  • 您可以创建一个实例,将其缓存在 "global" 中并多次重复使用,而不是每次 new-ing 一个异常对象。

  • 如果异常是自定义异常,您可以重写它的 fillInStacktrace 方法什么也不做。

在这两种情况下,如果您尝试打印堆栈跟踪,最终都会出现异常,这些异常会给您带来不正确的结果。但是,如果您使用异常来实现 "non-exceptional" 代码路径(因为您没有真正的选择!)那么这可能不相关。


我上面的解决方案有一个变体,其中自定义字节接收器 class 静默丢弃字节而不是抛出异常。这避免了异常的成本(但见上文),但它并没有避免 writeOut 在将大型 SOAP 消息序列化到具有 "stopped listening".[=22= 的字节接收器时所做的不必要的工作]


另一件需要注意的事情是,如果您使用的是 UTF-8 等多字节字符编码,则在一定数量的字节后截断可能会中断。考虑一下如果您在 100 个字节处截断并且第 100 个字节位于一个字符的中间会发生什么。请注意,如果 byte[] 包含在默认字符集中不是有效字符的序列,则 new String(byte[]) 的行为未指定。