java Spring Autowire 注释与几个接口实现

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17581882/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 02:23:30  来源:igfitidea点击:

Spring Autowire Annotation with Several Interface Implementations

javaspring

提问by Omar Al Kababji

Suppose you have one interface

假设你有一个界面

public interface A {
  public void doSomething();
}

and two implementation classes

和两个实现类

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

}

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

}

And finally a class that will use an "A" implementation:

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

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

Now if I want to inject AImpl1I add the @Qualifier("aImpl1")while if I want to inject AImpl2I add @Qualifier("aImpl2")

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

The question is:Is it possible to instruct spring somehow to look up all implementations of "A" in this case AImpl1and AImpl2and use some application specific conventions to choose the most appropriate implementation? for example in this case my convention could be use the implementation with the greatest suffix (i.e. AImpl2)?

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

EDIT:the class MyClass should not be aware at all about the implementation lookup logic, it should just find its property "a" set with an object of AImpl2.

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

采纳答案by garst

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 BeanDefinitionRegistryPostProcessorand implement either the method postProcessBeanDefinitionRegistryor postProcessBeanFactory.

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

This way you have access to allbean 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.

通过这种方式,您可以在实例化和注入之前访问所有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)

    }



}

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.

困难的部分是找到 bean 名称,这取决于您的应用程序的具体情况。我想拥有一致的命名约定会有所帮助。

Update:

更新:

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

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

On the other hand they are indeed available in postProcessBeanFactory.

另一方面,它们确实在postProcessBeanFactory.

回答by axtavt

You can inject all implentations as List:

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

@Autowired
List<A> as;

or as Mapwith bean name as key:

Map以 bean 名称为键:

@Autowired
Map<String, A> as; 

and then choose proper implementation manually (perhaps, in a setter method):

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

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

回答by digitaljoel

If you have a Configuration class you could use a method in that to make the decision of which implementation of A to return. Then the autowired will inject the appropriate instance for that class.

如果您有一个 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
    }
}

This assumes you always want to use the same instance everywhere you are injecting A. If not, then you could have different @Bean annotated methods with names to get different versions.

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

回答by daoway

You can try to use Spring Profiles.

您可以尝试使用Spring Profiles