11

假设你有一个接口

public interface A {
  public void doSomething();
}

和两个实现类

@Component(value="aImpl1")
public class AImpl1 implements A {

}

@Component(value="aImpl2")
public class AImpl2 implements A{

}

最后是一个将使用“A”实现的类:

@Component
public class MyClass {
  @Autowire
  A a;
}

现在如果我想注入AImpl1我添加@Qualifier("aImpl1")而如果我想注入AImpl2我添加@Qualifier("aImpl2")

问题是:在这种情况下AImpl1AImpl2是否可以指示 spring 以某种方式查找“A”的所有实现,并使用一些特定于应用程序的约定来选择最合适的实现?例如在这种情况下,我的约定可以使用具有最大后缀的实现(即 AImpl2)?

编辑:类 MyClass 根本不应该知道实现查找逻辑,它应该只找到它的属性“a”设置为 AImpl2 的对象。

4

4 回答 4

11

您可以将所有实现注入为List

@Autowired
List<A> as;

Map以 bean 名称作为键:

@Autowired
Map<String, A> as; 

然后手动选择正确的实现(也许在 setter 方法中):

@Autowired
public void setAs(Map<String, A> as) {
    this.a = ...;
}
于 2013-07-10T22:25:49.017 回答
5

Assuming you already have hundreds of interfaces and implementations (as you said in a comment), and you do not want to refactor all the code... then is a tricky problem... and this is a tricky solution:

You could create a custom BeanDefinitionRegistryPostProcessor and implement either the method postProcessBeanDefinitionRegistry or postProcessBeanFactory.

This way you have access to all bean definitions before they are instantiated and injected. Do your logic to find which is the preferred implementation for each one of your interfaces, and then, set that one as primary.

@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(
            BeanDefinitionRegistry registry) throws BeansException {

          // this method can be used to set a primary bean, although 
          // beans defined in a @Configuration class will not be avalable here.

    }

    @Override
    public void postProcessBeanFactory(
            ConfigurableListableBeanFactory beanFactory) throws BeansException {     

        // here, all beans are available including those defined by @configuration, @component, xml, etc.

        // do some magic to somehow find which is the preferred bean name for each interface 
        // you have access to all bean-definition names with: beanFactory.getBeanDefinitionNames()
        String beanName = "aImpl2"; // let's say is this one

        // get the definition for that bean and set it as primary
        beanFactory.getBeanDefinition(beanName).setPrimary(true)

    }



}

The hard part is to find the bean name, it depends of the specifics of your application. I guess that having a consistent naming convention will help.

Update:

It seems that both methods in the interface BeanDefinitionRegistryPostProcessor can be used for this purpose. Having in mind that in the postProcessBeanDefinitionRegistry phase, beans configured through @configuration classes are not yet available, as noted in the comments below.

On the other hand they are indeed available in postProcessBeanFactory.

于 2013-09-23T17:39:42.707 回答
2

如果你有一个 Configuration 类,你可以使用其中的一个方法来决定 A 的哪个实现返回。然后自动装配将为该类注入适当的实例。

@Configuration
public class ApplicationConfiguration {

    @Bean
    A getA() {
        // instantiate the implementation of A that you would like to have injected
        // or you could use reflection to find the correct class from the classpath.
        // return the instance
    }
}

这假设您总是希望在注入 A 的任何地方都使用相同的实例。如果不是,那么您可以使用不同的带有名称的 @Bean 注释方法来获得不同的版本。

于 2013-07-10T22:56:12.167 回答
0

您可以尝试使用Spring Profiles

于 2013-09-25T14:37:08.900 回答