弹簧自动装配基于服务的可用性
我有一个需要有条件地建立的,这取决于在运行时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有至少有机会尝试初始化。
Spring Profiles是你的朋友。您可以通过环境变量,命令行参数和其他方法设置当前配置文件。您可以注释一个Spring管理的组件,以便为特定配置文件创建它。
当我不知道我的应用程序何时开始我需要的配置文件时,配置文件不是答案。 –
公平的@NickJohnson,但是如果它有帮助的话,你也可以在运行时以编程方式设置配置文件,至少在Spring Boot中。 – RichW
确实。这种情况下的问题是一种鸡与鸡蛋 - 我不知道要选择哪种配置文件,直到我已经尝试初始化豆类为止......这意味着我需要知道选择哪个配置文件。 –
那么在这种情况下,原来是切向的错误,影响了整个错误的行为。
给予一定的背景,我的第一次,天真(但可行)的做法是这样的:
@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纠缠。
的教训在这里的是,如果你明确命名豆,一定要确保你的名字是独特在你的应用程序中,即使你打算换周围这样的事情。
你可以多说一下这三种实现是什么? –
举一个具体的例子,根据可用性,我可能有一个外部的Redis缓存,一个内存中的群集Hazelcast缓存,或者如果这两个都不可用,那么只是一个哑巴的本地HashMap(回退)。 根据我的具体情况,我将构建一个服务接口的具体实现,该接口知道如何获取/放置由任何可用服务提供的特定bean。 –
所以我可能会喜欢做的是一样的东西: 如果(Redis的豆)返回RedisImpl(Redis的豆) 否则,如果(hazelcast豆)返回HazelImpl(hazelcast豆) 否则返回DumbHashImpl() –