java - 以编程方式解决 Bean 注入(inject)

标签 java spring spring-boot spring-profiles

我想找到一种方法,在 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 默认组件。

参见: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");
}

关于java - 以编程方式解决 Bean 注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46928962/

相关文章:

Java 绘画问题

java - 是否有可能为特定的 JVM 实例获得真正唯一的 ID?

java - 如何从ajax get()方法调用的spring Controller 以json格式返回列表对象

postgresql - 表未在指定模式中创建

ssl - javax.net.ssl.SSLHandshakeException : java. security.cert.CertificateException:不存在主题替代名称

java - Wicket + Ajax + hasDatepicker

Java:测试服务的并发性

java - Spring AOP忽略Hessian Service的一些方法

spring - 组织.postgresql.util.PSQLException : Large Objects may not be used in auto-commit mode

java - 为什么spring websocket应用程序看不到html并抛出404错误?