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

Spring Boot依赖注入异常:子类对象被注入父类变量问题排查

问题描述

尝试实现工厂设计模式,根据传入参数返回不同类的对象:传入指定参数返回父类对象,另一参数返回子类对象。父类继承自抽象类,该抽象类实现了Interface接口。但传入父类对应的参数时,工厂返回的却是子类对象。调试发现,工厂构造函数调用时,父类变量中注入的已是子类对象。尝试使用Lombok的@RequiredArgsConstructor注解但无效果。

环境:Spring Boot 2.3.9.RELEASE、JDK 1.8,使用JUnit测试验证代码。

代码实现

Interface接口

public interface Interface {}

抽象父类

@RequiredArgsConstructor
@Component
public abstract class AbstractClass implements Interface{}

父类

@Slf4j
@Component("ParentClass")
public class ParentClass extends AbstractClass {}

子类

@Slf4j
@Component("ChildClass")
public class ChildClass extends ParentClass {}

工厂类

@Component
@Slf4j
public class Factory {
    private final ParentClass parentClass;
    private final ChildClass childClass;
    
    Factory(@Qualifier("ParentClass") ParentClass parentClass, @Qualifier("ChildClass") ChildClass childClass) {
        this.parentClass = parentClass;
        this.childClass = childClass;
    }
    
    public Interface getObject(boolean isParent){
            if(isParent)
                return parentClass;
            else
                return childClass;
    }
}

JUnit测试类

@RunWith(MockitoJUnitRunner.class)
public class FactoryTest {
    @InjectMocks
    private Factory factory;
    
    @Mock
    private ParentClass parentClass;
    
    @Mock
    private ChildClass childClass;
    
    @Test
    public void getObjectTest() {
        assertEquals(parentClass, factory.getObject(true));
    }
}
问题原因与解决方案

测试环境问题(Mockito注入错误)

你的测试使用MockitoJUnitRunner,当存在ParentClass和ChildClass两个mock时,由于ChildClass是ParentClass的子类,Mockito的@InjectMocks可能会错误地将ChildClass的mock注入到Factory的parentClass字段中(子类类型匹配父类类型),导致测试断言失败。

解决方法:

  • 给mock指定名称,并用@Qualifier匹配注入:
@RunWith(MockitoJUnitRunner.class)
public class FactoryTest {
    @InjectMocks
    private Factory factory;
    
    @Mock
    @Qualifier("ParentClass")
    private ParentClass parentClass;
    
    @Mock
    @Qualifier("ChildClass")
    private ChildClass childClass;
    
    @Test
    public void getObjectTest() {
        assertEquals(parentClass, factory.getObject(true));
    }
}
  • 或者改用构造函数注入的方式手动创建Factory实例,避免Mockito自动注入的歧义:
@RunWith(MockitoJUnitRunner.class)
public class FactoryTest {
    private Factory factory;
    
    @Mock
    private ParentClass parentClass;
    
    @Mock
    private ChildClass childClass;
    
    @Before
    public void setup() {
        factory = new Factory(parentClass, childClass);
    }
    
    @Test
    public void getObjectTest() {
        assertEquals(parentClass, factory.getObject(true));
    }
}

实际Spring运行环境问题

如果在实际运行时也出现父类变量注入子类对象的情况,原因是Spring容器中同时存在ParentClass和ChildClass两个bean,而ChildClass是ParentClass的子类,当Spring进行自动装配时,如果@Qualifier未正确生效,可能会注入子类实例。

解决方法:

  1. 确保@Qualifier注解的参数与@Component指定的bean名称完全一致(注意大小写)。
  2. 检查AbstractClass的@RequiredArgsConstructor是否影响子类构造:由于AbstractClass没有final或@NonNull字段,@RequiredArgsConstructor会生成无参构造函数,子类ParentClass和ChildClass可以正常实例化,这部分无需修改。
  3. 避免使用@Primary注解(容易引发其他装配歧义),优先保证@Qualifier的正确使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 20:12:51