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

Spring中能否注入某类型所有子类的Class对象列表?

解决Spring注入子类Class对象列表的问题

问题原因

你当前的代码试图注入List<Class<? extends StringProvider>>,但Spring容器中注册的是StringProvider类型的实例Bean,而非Class类型的Bean。Spring找不到匹配类型的Bean,因此抛出NoSuchBeanDefinitionException。

解决方案

方案1:从已实例化的Bean中提取Class对象

先注入所有StringProvider类型的Bean实例,再从中提取对应的Class。这种方式简单直接,适用于Bean已经实例化的场景:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = GenericTest.Config.class)
public class GenericTest {
    // 先注入所有StringProvider实例
    @Autowired
    private List<StringProvider> stringProviderBeans;
    private List<Class<? extends StringProvider>> stringProviderClasses;

    @PostConstruct
    public void init() {
        // 从实例中提取Class并去重,处理AOP代理获取真实目标类
        stringProviderClasses = stringProviderBeans.stream()
                .map(AopProxyUtils::ultimateTargetClass)
                .distinct()
                .collect(Collectors.toList());
    }

    @Test
    void test() {
        // 现在可以使用stringProviderClasses调用静态方法
        stringProviderClasses.forEach(clazz -> System.out.println(clazz.getName()));
    }

    interface StringProvider {
        String getString();
    }

    @Configuration
    static class Config {
        @Bean
        public StringProvider stringProviderOne() {
            return () -> "one";
        }

        @Bean
        public StringProvider stringProviderTwo() {
            return () -> "two";
        }
    }
}

方案2:直接从容器中获取Bean的注册类型(无需实例化)

如果你的Bean是懒加载的,或者不想提前实例化,可以通过BeanDefinitionRegistry获取所有注册的Bean类型,筛选出StringProvider的子类:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = GenericTest.Config.class)
public class GenericTest {
    @Autowired
    private BeanDefinitionRegistry beanDefinitionRegistry;
    private List<Class<? extends StringProvider>> stringProviderClasses;

    @PostConstruct
    public void init() throws ClassNotFoundException {
        stringProviderClasses = Arrays.stream(beanDefinitionRegistry.getBeanDefinitionNames())
                .map(beanDefinitionRegistry::getBeanDefinition)
                .map(BeanDefinition::getBeanClassName)
                .map(Class::forName)
                // 筛选StringProvider的子类,排除接口本身
                .filter(clazz -> StringProvider.class.isAssignableFrom(clazz) && !clazz.isInterface())
                .collect(Collectors.toList());
    }

    @Test
    void test() {
        stringProviderClasses.forEach(clazz -> System.out.println(clazz.getName()));
    }

    // 其余代码和之前一致...
}

注意事项

  • 如果你的StringProvider实现是Lambda表达式,其对应的Class是匿名内部类,通常没有自定义静态方法。如果需要调用静态方法,建议改用具体的类实现(而非Lambda)。
  • 若使用AOP代理,方案1中需要用AopProxyUtils.ultimateTargetClass(bean)获取真实的目标类,避免拿到代理类。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:35:37