2017-03-17 76 views
1

我有一個需要有條件地建立的,這取決於在運行時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() –

回答

0

Spring Profiles是你的朋友。您可以通過環境變量,命令行參數和其他方法設置當前配置文件。您可以註釋一個Spring管理的組件,以便爲特定配置文件創建它。

Spring Profiles from the Spring Documentation

+0

當我不知道我的應用程序何時開始我需要的配置文件時,配置文件不是答案。 –

+0

公平的@NickJohnson,但是如果它有幫助的話,你也可以在運行時以編程方式設置配置文件,至少在Spring Boot中。 – RichW

+0

確實。這種情況下的問題是一種雞與雞蛋 - 我不知道要選擇哪種配置文件,直到我已經嘗試初始化豆類爲止......這意味着我需要知道選擇哪個配置文件。 –

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糾纏。

的教訓在這裏的是,如果你明確命名豆,一定要確保你的名字是獨特在你的應用程序中,即使你打算換周圍這樣的事情。