以编程方式解决 Bean 注入

Resolve Bean Injection Programmatically

你好,我想找到一种方法,以编程方式在 Spring 容器级别的 bean 工厂中存在的不同实现之间选择一个 Candidate。

使用配置文件我可以像这样简单地实现

我有以下配置文件 "CH" 和 "IT",我希望如果存在使用 IT 配置的 Bean,那么将回退到默认值。

鉴于那些 classes:

默认实现

@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,这很公平,因为默认值没有被分析,并且它将被容器注册而不需要任何进一步的关注。

一些考虑:

如果我将 @Profile("default") 添加到 DefaultMapper 这将有效,直到我有另一个默认 bean 有一个子 class 仅使用 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,这很公平,但不那么公平,因为 "default" 没有链接为 ActiveProfiles 并且对于我没有实施。此处的 CH 配置文件将起作用,因为每个默认值都有一个配置文件实现 class.

说过 我想要一种更好的方法来涵盖这种情况,而无需: - 必须在默认 类 上定义 @Profile({"default","IT", "<all other profiles which have NOT a specialized implementation"}@Profile("!CH") ,其中我排除了那些具有专业化的配置文件。 - @Primary 我认为这不是灵魂,因为在 DefaultMapper 上我必须进行专业化,这将被标记为 @Primary 而容器根本不喜欢它 ;)

然后 我想解决这个问题,我可以决定所有注入的 类,当 Beans 的分辨率不明确时,我想以编程方式决定(在容器中)我想选择哪个实现。我认为与 @Profile 类似的注释,即 @Candidate("CH") 将适合与 application.properties 中设置的值相比,即 nationality.cantidate=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 扩展的东西,它很优雅,我也可以用来自 CDI 的 @Specializes 以另一种方式覆盖它,但我找不到同样简单的方法 Spring(我没有那么多经验),BeanFactory? BeanFactoryPostProcessor ?

求助?

您可以将所有特定的解析器标记为@Primary 这将确保Spring 自动装配这个解析器而不是未标记为@Primary 的默认解析器。当所选配置文件不存在特定解析器时,它将回退到非 @Primary 默认组件。

参见:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Primary.html

@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");
}

您可以激活多个配置文件、CH 和 IT,您的实施将失败。

这是一个国际化问题。