具有多个接口实现的Spring Autowire注释

时间:2013-07-10 22:15:28

标签: java spring

假设您有一个界面

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”)

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

编辑: MyClass类根本不应该了解实现查找逻辑,它应该只找到一个带有AImpl2对象的属性“a”。

4 个答案:

答案 0 :(得分:10)

您可以将所有实施注入List

@Autowired
List<A> as;

或以{bean}为密钥的Map

@Autowired
Map<String, A> as; 

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

@Autowired
public void setAs(Map<String, A> as) {
    this.a = ...;
}

答案 1 :(得分:5)

假设您已经拥有数百个接口和实现(正如您在评论中所述),并且您不想重构所有代码......那么这是一个棘手的问题......这是一个棘手的解决方案:< / p>

您可以创建自定义BeanDefinitionRegistryPostProcessor并实施方法postProcessBeanDefinitionRegistrypostProcessBeanFactory

这样,您可以在实例化和注入之前访问所有 bean定义。您的逻辑是为了找到每个接口的首选实现,然后将其设置为 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)

    }



}

困难的部分是找到bean名称,它取决于你的应用程序的细节。我想拥有一致的命名约定会有所帮助。

<强>更新

接口BeanDefinitionRegistryPostProcessor中的两个方法似乎都可以用于此目的。请记住,在postProcessBeanDefinitionRegistry阶段,通过@configuration类配置的bean尚不可用,如下面的评论中所述。

另一方面,postProcessBeanFactory确实可以使用它们。

答案 2 :(得分: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注释方法来获得不同的版本。

答案 3 :(得分:0)

您可以尝试使用Spring Profiles