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

Spring Boot测试时Predicate中@Autowired注入的repository为null如何实现构造注入

问题根因

你当前使用的是字段注入(@Autowired直接打在成员变量上),只有当NameChangePredicate的实例是由Spring容器管理创建的时候,Spring才会自动注入依赖的NameChangeRepository。但你在测试代码里是直接通过new NameChangePredicate("", zone)手动创建的实例,这个实例不受Spring管控,所以被@Autowired标注的repository自然不会被赋值,始终为null。
生产环境能正常运行,大概率是生产环境里的NameChangePredicate实例是Spring自动创建注入的,没有手动new的操作。

改造方案:构造器注入适配自定义参数

完全可以通过构造器注入同时满足Spring依赖注入和自定义参数传递的需求,改造步骤如下:

1. 改造Predicate类

将需要Spring注入的Repository和自定义参数都放到构造器参数里,Spring 4.3+之后,构造器参数里的Bean会自动注入,不需要额外加@Autowired:

@Slf4j
public class NameChangePredicate implements ValidationPredicate {

    private final NameChangeRepository nameChangeRepository;
    private final String errorCode;
    private final GeographicZone zone;

    // 构造器同时接收Spring托管的Repository和自定义参数
    public NameChangePredicate(NameChangeRepository nameChangeRepository, String errorCode, GeographicZone zone) {
        this.nameChangeRepository = nameChangeRepository;
        this.errorCode = errorCode;
        this.zone = zone;
    }

    /**
     * Test the condition
     *
     * @param response the {@link ValidationServiceResponse}
     * @return true if valid
     */
    @Override
    public boolean test(ValidationServiceResponse response) {
        if (!StringUtils.isBlank(response.getBookingClass()) && zone != null) {
            List<NameChange> nameChanges = nameChangeRepository.findAllByGeographicZoneId(zone.getId());

            if (nameChanges.stream().anyMatch(n -> n.getClasses().stream().anyMatch(response.getBookingClass()::equalsIgnoreCase))) {
                return true;
            }
        }

        throw new ValidationException(this.errorCode);
    }
}

注意:你原来的代码里调用了response.getClass(),这是Object自带的获取类对象的方法,和你实际要取的预订舱位属性冲突,这里已经修正为getBookingClass()和你测试代码里的set方法对应,避免低级错误。

2. 改造测试代码

创建实例的时候把测试类里已经注入成功的repository手动传入即可:

@ActiveProfiles("test")
@SpringBootTest
public class NameChangeTest {

    @Autowired
    NameChangeRepository nameChangeRepository;

    @Autowired
    GeographicZoneRepository geographicZoneRepository;

    private GeographicZone zone;

    @BeforeEach
    public void init() {
        // 你的初始化逻辑
    }

    @Test
    public void testNameChangeParameter() {
        // 手动传入已经注入成功的repository
        final NameChangePredicate predicate = new NameChangePredicate(nameChangeRepository, "", zone);
        List<NameChange> list = nameChangeRepository.findAll();
        Assert.assertEquals("There should be 4 NameChange in db", 4, list.size());

        ValidationServiceResponse response = new ValidationServiceResponse();
        response.setBookingClass("C");
        Assert.assertTrue("Name change should be eligible", predicate.test(response));
    }
}

3. 生产环境适配

如果生产环境你需要动态创建不同参数的NameChangePredicate,可以封装一个工厂类来托管Repository,简化创建流程:

@Component
public class NameChangePredicateFactory {
    // 工厂类由Spring管理,自动注入Repository
    private final NameChangeRepository nameChangeRepository;

    public NameChangePredicateFactory(NameChangeRepository nameChangeRepository) {
        this.nameChangeRepository = nameChangeRepository;
    }

    // 对外提供创建实例的方法,只需要传自定义参数
    public NameChangePredicate newInstance(String errorCode, GeographicZone zone) {
        return new NameChangePredicate(nameChangeRepository, errorCode, zone);
    }
}

生产环境使用的时候直接注入工厂类,调用newInstance方法即可,不需要再手动传Repository:

@Autowired
private NameChangePredicateFactory predicateFactory;

// 业务代码里创建实例
NameChangePredicate predicate = predicateFactory.newInstance("ERROR_CODE_001", currentZone);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 14:15:03