You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JDK17中Spy ByteArrayOutputStream致对象上下文丢失引发空指针问题

问题:JDK11升级至17+Mockito版本升级后,Spy ByteArrayOutputStream触发空指针异常

问题背景

我们将JDK从11升级到17,同时把Mockito从3.3.3版本升级至4.11.0后,原有正常运行的测试用例失效。该测试通过Spy ByteArrayOutputStream 对象来验证其未被关闭,执行时触发空指针异常。

测试代码

@Test
void encryptionDoesNotCloseOutputStream() throws Throwable {
    byte[] key = new byte[32];
    RANDOM.nextBytes(key);
    byte[] data = new byte[101];
    RANDOM.nextBytes(data);
    ByteArrayInputStream in = spy(new ByteArrayInputStream(data));
    ByteArrayOutputStream out = spy(new ByteArrayOutputStream());

    ...
    
    // Write the IV at the beginning of the file (first 12 Bytes)
    out.write(buffers.iv);
    
    ...

    verify(in, never()).close();
    verify(out, never()).close();
}

报错信息

[ERROR] Errors:
[ERROR] CryptoUtilsTest.encryptionDoesNotCloseOutputStream:47 » NullPointer Cannot read the array length because "this.buf" is null

已尝试的排查动作

  • 替换Mockito依赖为mockito-inline,问题未解决
  • 单独升级JDK至17不改动Mockito时,问题依然出现;暂未验证仅升级Mockito在JDK11环境下是否触发该问题

解决方案

方案1:自定义跟踪类替代Spy

避免直接Spy ByteArrayOutputStream,改用自定义子类跟踪关闭状态:

class TrackedByteArrayOutputStream extends ByteArrayOutputStream {
    private boolean closed = false;

    @Override
    public void close() throws IOException {
        closed = true;
        super.close();
    }

    public boolean isClosed() {
        return closed;
    }
}

测试代码调整为:

@Test
void encryptionDoesNotCloseOutputStream() throws Throwable {
    // ...其他初始化逻辑
    TrackedByteArrayOutputStream out = new TrackedByteArrayOutputStream();

    // ...执行测试逻辑

    // 验证未关闭
    assertFalse(out.isClosed());
    verify(in, never()).close();
}

方案2:使用Mockito委托模式替代Spy

如果依赖Mockito的验证逻辑,可以通过delegatesTo委托实际实现,避免Spy具体类:

@Test
void encryptionDoesNotCloseOutputStream() throws Throwable {
    // ...其他初始化逻辑
    ByteArrayOutputStream delegateOut = new ByteArrayOutputStream();
    OutputStream out = mock(OutputStream.class, AdditionalAnswers.delegatesTo(delegateOut));

    // ...执行测试逻辑,out的方法调用会委托给delegateOut

    verify(in, never()).close();
    verify(out, never()).close();
}

原因分析

JDK17对ByteArrayOutputStream的内部实现(如buf字段的访问逻辑、生命周期管理)做了调整,Mockito的Spy机制在代理该类时,无法正确维护内部字段的状态,导致后续调用write等方法时触发空指针异常。单独升级JDK17就出现问题,说明核心诱因是JDK版本升级带来的类内部变化,Mockito的版本升级进一步叠加了该问题。

内容的提问来源于stack exchange,提问作者Lars van roy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 12:20:42