【问题标题】:Resolve Bean Injection Programmatically以编程方式解决 Bean 注入
【发布时间】:2017-10-25 09:37:29
【问题描述】:

您好,我想找到一种方法,以编程方式在 Spring 容器级别的 bean 工厂上的不同实现之间选择候选人。

使用 Profiles 我可以像这样简单地实现这一目标

我有以下配置文件“CH”和“IT”,我希望如果有一个带有 IT 配置的 Bean,那么将回退到默认值。

鉴于这些类:

默认实现

@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 的子类仅使用 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 并且容器根本不喜欢它;)

然后 我想解决这个问题,我可以决定所有注入的类,当 Bean 的分辨率不明确时,我想以编程方式决定(在容器中)我想选择哪个实现。我认为与@Profile 类似的注释,即@Candidate("CH") 将适合与在i.E nationality.cantidate=CH 中的application.properties 中设置的值相比。比如:

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 ?

帮助?

【问题讨论】:

    标签: java spring spring-boot spring-profiles


    【解决方案1】:

    您可以将所有特定的 Parsers 标记为 @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");
    }
    

    【讨论】:

    • 在演示中似乎可以正常工作,我曾尝试过类似的操作,但无法正常工作,我会再试一次。 ` @Primary @Profile(value = { MigrationProfiles.DEFAULT }) @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface PrimaryProfiles { @AliasFor(annotation = Profile.class,属性=“值”)字符串[]值(); } `
    • 幸运的是我很愚蠢,在我的主要场景中,我忘记在默认类上删除@Primary。感谢您再次将我带到这个世界。
    • 没问题,很高兴我能帮上忙
    • 好的,它可以工作,我的默认是干净的,相反我的 AliasFor 注释没有用,它不会像这样工作:( 我需要放置 Primary 和 Profile,不能聚合注释,至少那些注释...
    【解决方案2】:

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

    这是一个国际化问题。

    【讨论】:

    • 这是一个例子,无论如何,如果你运行多个国家,它应该会失败。
    猜你喜欢
    • 2014-02-27
    • 2017-10-02
    • 2011-10-08
    • 2020-08-12
    • 1970-01-01
    • 1970-01-01
    • 2014-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多