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

如何模拟带有@PostConstruct方法的Bean?解决初始化阶段空指针异常问题

解决@PostConstruct中的NullPointerException问题

你的问题根源在于Mockito直接mockMyClass时,不会执行原类的构造器逻辑,导致mapper字段未被赋值,而Spring会在Bean创建后调用@PostConstruct方法,这时自然触发空指针异常。

下面提供两种可行的解决方案,推荐第一种更简洁的方式:

方案一:用Mockito.spy包装真实的MyClass实例

既然你需要MyClass的mock对象,但又要保留它的构造器注入和@PostConstruct初始化逻辑,可以创建真实的MyClass实例(传入ObjectMapper),再用spy包装它——这样既保留了正常的初始化流程,又能mock你需要的方法。

修改你的Config类中的myClass()方法:

@Bean
MyClass myClass(ObjectMapper mapper) {
    // 创建真实实例并spy,构造器会被调用,mapper被注入
    return Mockito.spy(new MyClass(mapper));
}

这样处理后,MyClass的@PostConstruct方法执行时,mapper已经被正确赋值,不会再出现空指针;同时这个spy对象可以正常注入到MyService中,你也可以用Mockito的API来mock它的其他方法行为。

方案二:手动为mock对象注入依赖并触发初始化

如果你必须使用纯mock(不创建真实实例),可以通过反射为mock对象设置mapper字段,再手动调用@PostConstruct方法:

import org.springframework.test.util.ReflectionTestUtils;
import java.lang.reflect.Method;

// ...

@Bean
MyClass myClass(ObjectMapper mapper) {
    MyClass myClassMock = Mockito.mock(MyClass.class);
    
    // 用反射设置mapper字段
    ReflectionTestUtils.setField(myClassMock, "mapper", mapper);
    
    // 手动调用@PostConstruct的init方法
    try {
        Method initMethod = MyClass.class.getDeclaredMethod("init");
        initMethod.setAccessible(true);
        initMethod.invoke(myClassMock);
    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
        throw new RuntimeException("Failed to execute @PostConstruct method", e);
    }
    
    return myClassMock;
}

这种方式需要处理反射相关的异常,而且如果@PostConstruct方法的签名或访问权限变化,代码需要同步调整,所以仅在特殊场景下使用。

为什么原代码会出错?

当你调用Mockito.mock(MyClass.class)时,Mockito会生成一个动态代理对象,这个对象不会调用原类的构造器,因此MyClass中的mapper字段始终为null。而Spring会在Bean注册完成后自动调用@PostConstruct方法,这时访问mapper就会触发NullPointerException。

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 18:27:29