我想找到一种方法,在 Spring 容器级别的 bean 工厂上存在的不同实现之间以编程方式选择候选者。
通过配置文件,我可以像这样简单地实现这一点
我有以下配置文件“CH”和“IT”,并且我希望如果存在使用 IT 进行配置文件的 Bean,则将回退到默认值。
鉴于这些类(class):
默认实现
@Component
public class DefaultMapper {
public void doSomething() {
System.out.println("Map Default");
}
}
瑞士实现
@Component
@Profile("CH")
public class SwissMapper extends DefaultMapper {
@Override
public void doSomething() {
System.out.println("do something swiss");
}
}
意大利实现:
@Component
@Profile("IT")
public class ItalianMapper extends DefaultMapper {
@Override
public void doSomething() {
System.out.println("do pasta");
}
}
现在如果我用 @ActiveProfiles("IT")
运行它它会抛出 NoUniqueBeanDefinitionException,这很公平,因为没有对 Default 进行分析,并且它将在容器不做任何进一步关注的情况下进行注册。
一些考虑:
如果我添加 @Profile("default")
至 DefaultMapper
这将一直有效,直到我有另一个默认 bean 的子类仅使用 CH 进行分析。
另一个 Bean 的默认实现:
@Component
@Profile("default")
public class DefaultParser {
public void doSomething() {
System.out.println("Parse Default");
}
}
然后是瑞士实现(没有可用的意大利语,或者更好地说默认值适合意大利语):
@Component
@Profile("CH")
public class SwissParser extends DefaultParser {
@Override
public void doSomething() {
System.out.println("Ässe Ässe");
}
}
现在如果我用 @ActiveProfiles("IT")
运行它它抛出一个 No BeanDefinitionException ,这足够公平,但不是那么公平,因为“默认”没有链接为 ActiveProfiles 并且对于 IT 我没有实现。这里的 CH 配置文件将起作用,因为每个默认类都有一个配置文件实现。
这么说 我想要一种更好的方法来涵盖这种情况,而无需:
- 必须在默认类上定义,例如
@Profile({"default","IT", "<all other profiles which have NOT a specialized implementation"}
,或@Profile("!CH")
我排除了那些具有特化的个人资料。 -
@Primary
我认为不是一个解决方案,因为关于DefaultMapper
我必须特化,它将被标记为@Primary
而且容器根本不喜欢它
然后 我想解决这个问题,我可以决定所有注入(inject)的类,当 Bean 的解析不明确时,我想以编程方式决定(在容器中)我想要选择哪个实现。我认为注释与 @Profile 类似,即@Candidate("CH") 将适合与 i.E. 中 application.properties 中设置的值进行比较。国籍.候选人=CH.像这样的东西:
public class MyExtension extends UnknowSpringType {
@Override
Object resolveAmbigousDependency(final Context ctx, final Resolution resolution) {
final List<Class> clazzes = resolution.getResolvedClasses();
for (final Class clazz : clazzes) {
if (clazz.isAnnotationPresent(Candidate.class) && clazz.getAnnotation(Candidate.class).getVaue().equals(candidateApplicationPropertyValue)) {
return clazz;
}
return getDefaultImplementation(clazzes);
}
}
我过去做过一些与 CDI SPI 扩展类似的事情,它很优雅,而且我也可以用 @Specializes
以另一种方式覆盖它。来自CDI,但我在Spring上找不到同样简单的方法(我对此没有太多经验),BeanFactory? BeanFactoryPostProcessor ?
帮忙?
最佳答案
您可以将所有特定的解析器标记为 @Primary,这将确保 Spring Autowiring 此解析器,而不是未标记为 @Primary 的默认解析器。当所选配置文件不存在特定解析器时,它将回退到非 @Primary 默认组件。
@Component
public class DefaultParser {
public void doSomething() {
System.out.println("Parse Default");
}
}
@Component
@Primary
@Profile("CH")
public class SwissParser extends DefaultParser {
@Override
public void doSomething() {
System.out.println("Ässe Ässe");
}
关于java - 以编程方式解决 Bean 注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46928962/