基于服务可用性的Spring自动装配

时间:2017-03-17 19:44:59

标签: spring autowired

我需要有条件地创建服务的三种可能实现之一,具体取决于Spring应用程序在运行时检测到的环境。如果Service A可用,那么我想创建一个使用Service A作为依赖项的具体实现类。如果服务A不可用,那么我想使用服务B作为依赖项创建一个实现。等等。

依赖于实现的类将自动装配接口,而不关心为特定环境选择的底层服务是什么。

我对此的第一个尝试是实现多个@Bean方法,这些方法返回bean或null,具体取决于Service是否可用,然后有一个单独的@Configuration类@Autowire(required = false)这两个可能的服务,有条件地创建实现,具体取决于哪个@Autowired字段不为null。

这里的问题是,当required = false时,Spring似乎并不关心它是否在等待构建候选者;也就是说,尝试选择实现的类可能在构造一个或两个required = false Beans之前构造,从而确保一个或两个可能总是为null,无论它是否可以设法正确初始化。

这种感觉就像我在这一点上反对,所以我正在寻找关于"对"的建议。做这种事情的方法,可以根据一些外部服务或环境的可用性来切换整套bean。

配置文件看起来不是正确答案,因为在我的服务bean尝试初始化我想要选择的实现之前,我不会知道;在创建上下文时,我当然不会知道它。

@Order也没有达到目标。 @Conditional也没有测试bean的存在(因为它仍然可能尚未构建)。与FactoryBean相同的问题 - 检查在FactoryBean被要求创建实例时可能尚未构造的bean的存在是没有用的。

我真正需要做的是根据其他bean的可用性创建一个Bean,但只有这些bean至少有机会尝试初始化。

2 个答案:

答案 0 :(得分:0)

Spring Profiles是你的朋友。您可以通过环境变量,命令行参数和其他方法设置当前配置文件。您可以注释Spring管理的组件,以便为特定的配置文件创建它。

Spring Profiles from the Spring Documentation

答案 1 :(得分:0)

在这种情况下,事实证明这是一个影响整个错误行为的切线错误。

为了给出一些背景知识,我的第一个,天真(但可行)的方法看起来像这样:

@Autowired(required=false)
@Qualifier(RedisConfig.HISTORY)
private RLocalCachedMap<String, History> redisHistoryMap;

@Autowired(required=false)
@Qualifier(HazelcastConfig.HISTORY)
private IMap<String, History> hazelcastHistoryMap;

// RequestHistory is an interface
@Bean
public RequestHistory requestHistory() {
    if (redisHistoryMap != null) {
        return new RedisClusteredHistory(redisHistoryMap);
    } else if (hazelcastHistoryMap != null) {
        return new HazelcastClusteredHistory(hazelcastHistoryMap);
    } else {
        return new LocalRequestHistory();  // dumb hashmap
    }
}

在其他@Configuration类中,如果此处获得@Autowired的bean不可用(由于缺少配置,初始化期间的异常等),创建它们的@Bean方法将返回空。

观察到的行为是在调用@Bean RLocalCachedMap<>方法后调用此@Bean方法,但在之前 Spring试图通过调用IMap<>方法来创建@Bean。我错误地认为这与required=false有关,但实际上与它无关。

实际发生的事情是我偶然为两个@Bean名称(以及@Qualifier s)使用了相同的常量,所以可能Spring在计算其依赖图时无法区分它们对于这个@Configuration类...因为两个@Autowire d bean看起来是一样的(因为它们具有相同的名称)。

(在这种情况下使用@Qualifier的次要原因是我不会进入这里,但只需说它可以有很多地图相同的类型。)

一旦我更好地对这些名字进行了限定,代码就完全符合我的要求,尽管它的方式有些不雅/丑陋。

在某些时候,我会回去看看它是否更优雅/更难看,并且使用@Conditional@Primary代替if / else犯规也是如此。< / p>

这里的教训是,如果您明确命名bean,请确保您的应用程序中的名称​​ unique ,即使您打算像这样交换东西。