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

问题描述:

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

类依赖于实施,将自动装配接口并不太在乎得到了什么选择的基本服务,对于特定的环境。

我第一次尝试这种做法是实现多个@Bean方法,根据服务是否可用返回一个bean或null,然后有一个单独的@Configuration类@Autowire(required = false)两个可能的服务,根据哪个@Autowired字段不是null来有条件地创建实现。

这里的问题是,需要= FALSE时,春天似乎并不关心它是否等待周围将建造候选人;也就是说,尝试选择实现的类可能会在构建required或false Beans之前被构造,从而确保其中一个或两个可能始终为空,无论它是否可以正确初始化。

它那种感觉就像我逆着在这一点粮食,所以我需要建议的“正确”的方式做这样的事情,在这里一整套豆可能会切换出基于某些外部服务或环境的可用性。

型材不喜欢看正确的答案,因为我不知道,直到后,我的服务豆尝试初始化我想选择哪一个实现;我在创建背景时当然不会知道它。

@Order没有任何实现的目标。 @Conditional并没有对这个bean的存在进行测试(因为它还没有被构造)。与FactoryBean同样的问题 - 检查FactoryBean被要求创建实例时可能没有构建的Bean的存在没有意义。

我真正需要做的是基于其他豆类的可用性创建一个bean,但前提是这些bean有至少有机会尝试初始化。

+0

你可以多说一下这三种实现是什么? –

+0

举一个具体的例子,根据可用性,我可能有一个外部的Redis缓存,一个内存中的群集Hazelcast缓存,或者如果这两个都不可用,那么只是一个哑巴的本地HashMap(回退)。 根据我的具体情况,我将构建一个服务接口的具体实现,该接口知道如何获取/放置由任何可用服务提供的特定bean。 –

+0

所以我可能会喜欢做的是一样的东西: 如果(Redis的豆)返回RedisImpl(Redis的豆) 否则,如果(hazelcast豆)返回HazelImpl(hazelcast豆) 否则返回DumbHashImpl() –

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

Spring Profiles from the Spring Documentation

+0

当我不知道我的应用程序何时开始我需要的配置文件时,配置文件不是答案。 –

+0

公平的@NickJohnson,但是如果它有帮助的话,你也可以在运行时以编程方式设置配置文件,至少在Spring Boot中。 – RichW

+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方法返回null。

观察的行为,这@Bean方法获取调用的RLocalCachedMap<>@Bean方法接到电话后,但春节前试图通过调用其@Bean方法来创建IMap<>。我错误地认为这与required=false有关,但实际上与它无关。

实际发生的是什么,我不小心同时用于@Bean名(因此@Qualifier S)相同的常量,因此想必春天不能看出其中的差别,当它被用于计算该@Configuration类的依赖关系图......因为两个@Autowire d豆似乎是相同的东西(因为它们具有相同的名称)。

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

一旦我合格更好的名字,代码做了我想要的东西,虽然有点不雅/丑陋。

在某些时候,我会回去看看,如果它看起来更优雅/更少丑陋和作品一样好使用@Conditional@Primary代替的if/else纠缠。

的教训在这里的是,如果你明确命名豆,一定要确保你的名字是独特在你的应用程序中,即使你打算换周围这样的事情。