如何获取指定接口的所有bean,它也实现了其他接口

How to get all beans of specified interface which also implements other interfaces

这里我有 3 个接口:InterfaceAInterfaceBSharedInterface

public interface InterfaceA {
    /**
     * print some message
     */
    void printMsg();
}
public interface InterfaceB {
    /**
     * print some message
     */
    void printMsg();
}
public interface SharedInterface {
    /**
     * print some message
     */
    void printSharedMsg();
}

这些接口有 3 个实现:

public class ImplementA1 implements InterfaceA, SharedInterface {
    @Override
    public void printMsg() {
        System.out.println("this is message of interfaceA1");
    }

    @Override
    public void printSharedMsg() {
        System.out.println("this is shared message from ImplementA1");
    }
}
public class ImplementA2 implements InterfaceA, SharedInterface {
    @Override
    public void printMsg() {
        System.out.println("this is message of interfaceA2");
    }

    @Override
    public void printSharedMsg() {
        System.out.println("this is shared message from ImplementA2");
    }
}
public class ImplementB implements InterfaceB, SharedInterface {
    @Override
    public void printMsg() {
        System.out.println("this is message of interfaceB");
    }

    @Override
    public void printSharedMsg() {
        System.out.println("this is shared message from ImplementB");
    }
}

ImplementA1ImplementA2是同一种运算,ImplementB是另一种运算。所以我决定开发 2 个配置 class 来注册 ImplementA1、ImplementA2 和 ImplementB,如下所示。

@Configuration
public class InterfaceAConfig {
    @Bean
    public InterfaceA registerInterfaceA1(){
        return new ImplementA1();
    }

    @Bean
    public InterfaceA registerInterfaceA2(){
        return new ImplementA2();
    }
}
@Configuration
public class InterfaceBConfig {
    @Bean
    public InterfaceB registerInterfaceB(){
        return new ImplementB();
    }
}

现在我想让所有实现 SharedInterface 的 bean 在组件中打印它们的消息。它运行良好,这是代码:

@Component
@AutoConfigureAfter(value = {
    InterfaceAConfig.class,
    InterfaceBConfig.class})
public class SharedInterfaceComponent implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
    private ApplicationContext applicationContext;

    //print shared message after IOC container refreshed
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        usingContextGetBean();
    }

    private void usingContextGetBean() {
        Map<String, SharedInterface> beans = this.applicationContext.getBeansOfType(SharedInterface.class);
        System.out.println(beans.size());
        for (SharedInterface bean : beans.values()) {
            bean.printSharedMsg();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

但我找到了另一种将 bean 注入组件的方法,使用

@Autowired
List<TargetType> myListName

所以我决定将我的 SharedInterfaceComponent 改成这个进行测试,它起作用了:

@Component
@AutoConfigureAfter(value = {
    InterfaceAConfig.class,
    InterfaceBConfig.class})
public class SharedInterfaceComponent  implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
    private ApplicationContext applicationContext;

    //todo why do spring failed due to this autowire?
    @Autowired
    private List<InterfaceA> autowiredList;

    //print shared message after IOC container refreshed
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        usingAutowiredGerBean();
        //usingContextGetBean();
    }

    private void usingAutowiredGerBean() {
        for (InterfaceA interfaceA : autowiredList) {
            if (SharedInterface.class.isAssignableFrom(interfaceA.getClass())){
                ((SharedInterface) interfaceA).printSharedMsg();
            }
        }
    }

    private void usingContextGetBean() {
        Map<String, SharedInterface> beans = this.applicationContext.getBeansOfType(SharedInterface.class);
        System.out.println(beans.size());
        for (SharedInterface bean : beans.values()) {
            bean.printSharedMsg();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

但是当我尝试使用 SharedInterface 而不是 InerfaceA 从 IOC 获取 bean 时,它出错了。代码如下所示:

@Component
@AutoConfigureAfter(value = {
    InterfaceAConfig.class,
    InterfaceBConfig.class})
public class SharedInterfaceComponent  implements ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
    private ApplicationContext applicationContext;

    //todo why do spring failed due to this autowire?
    @Autowired
    private List<SharedInterface> autowiredList;

    //print shared message after IOC container refreshed
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        usingAutowiredGerBean();
        //usingContextGetBean();
    }

    private void usingAutowiredGerBean() {
        for (SharedInterface sharedInterface : autowiredList) {
            if (SharedInterface.class.isAssignableFrom(sharedInterface.getClass())){
                ((SharedInterface) sharedInterface).printSharedMsg();
            }
        }
    }

    private void usingContextGetBean() {
        Map<String, SharedInterface> beans = this.applicationContext.getBeansOfType(SharedInterface.class);
        System.out.println(beans.size());
        for (SharedInterface bean : beans.values()) {
            bean.printSharedMsg();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

在此演示中,应用程序将失败并显示

***************************
APPLICATION FAILED TO START
***************************

Description:

Field autowiredList in com.wwstation.test.config.SharedInterfaceComponent required a bean of type 'java.util.List' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'java.util.List' in your configuration.


但是在我的其他项目中,同样的情况不会导致暗恋,我可以通过@Autowired获得SharedInterface但是我只能获得bean实现InterfaceA或者InterfaceB 但不是全部。我想,不崩溃的情况可能是我在其他项目中的一些依赖造成的。 任何人都可以帮助我如何让所有 SharedInterface 更优雅?非常感谢!

问题出在你的配置上。

@Bean
public InterfaceA registerInterfaceA1(){
    return new ImplementA1();
}

问题是 Spring 将使用 return 类型的方法来查看它是否填满注入点(在本例中为您的列表)。由于 InterfaceA 不是 SharedInterface 最终它会失败,因为没有根据您的配置实现 SharedInterface 的 bean!

你应该对你自己的bean做的是在return类型中尽可能具体。因此,将 InterfaceA 改为 return 实际的 class ImplementA1ImplementA2。这样 Spring,在配置时,可以确定那些实现 SharedInterface 并使用它们来填充列表。

@Bean
public ImplementA1 registerInterfaceA1(){
    return new ImplementA1();
}