我有一個介面和多個實作。我在類中自動連接介面以供使用。我需要在運行時選擇不同的實作。
public class Util {
public void getClient();
}
實作
public class UtilOne implements Util {
public void getClient() {...}
}
public class UtilTwo implements Util {
public void getClient() {...}
}
@Configuration
public class AppConfig {
@Autowired
@Bean
@Primary
public Util utilOne() {
return new UtilOne();
}
@Autowired
@Bean
public Util utilTwo() {
return new UtilTwo();
}
}
@Component
public class DemoService {
@Autowired
private Util util;
}
出于某種原因,如果我們無法在 UtilOne 中獲取客戶端,我想在不重新啟動應用程式的情況下切換到 UtilTwo。我想將 DemoService 中的 Util 物件更改為 UtilTwo 物件。屬性active.util將來自資料庫,我們可以從 UI 更新。
uj5u.com熱心網友回復:
它不是這樣作業的——如果你有一個特定的 Util 實作連接到類 SampleClass (它是一個單例),你不能在不重新啟動應用程式背景關系的情況下真正將 Util 的實作更改為不同的東西。
因此,我建議不要采用這種方式,而是建議另一種方法。您說在運行時評估的某些條件下,您想要切換實作。它是什么樣的條件?是否可以提取此條件決策邏輯?
如果是這樣,您可以自動裝配一個特殊的 DynamicUtil 來保存對所有實用程式的參考,并根據條件呼叫所需的實用程式:
// represents all possible business 'runtime' outcomes
enum ConditionOutcome {
A, B, C
}
interface ConditionEvaluator {
ConditionOutcome evaluate(); // when called in runtime will evaluate a condition that currently exists in the system
}
interface Util {
void foo();
ConditionOutcome relevantOfOutcome();
}
class Utill1Impl implements Util {
public void foo() {...}
public ConditionOutcome relevantOfOutcome() {return ConditionOutcome.A;}
}
class Utill2Impl implements Util {
public void foo() {...}
public ConditionOutcome relevantOfOutcome() {return ConditionOutcome.B;}
}
class Utill3Impl implements Util {
public void foo() {...}
public ConditionOutcome relevantOfOutcome() {return ConditionOutcome.C;}
}
class DynamicUtil {
private final Map<ConditionOutcome, Util> possibleImpls;
private final ConditionEvaluator evaluator;
public class DynamicUtil(List<Util> allImplementations, ConditionEvaluator evaluator) {
// create a map by calling the 'relevantOfOutcome' per util impl in a loop
this.evaluator = evaluator;
}
public void foo() {
ConditionOutcome key = evaluator.evaluate();
// pick the relevant implementation based on evaluated key
possibleImpls.get(key).foo();
}
}
現在通過這樣的設計,您可以動態添加新的可能結果(以及應該實作它們的實用程式。DynamicUtil盡管您系統中的類必須自動裝配,因此您將有效地引入一個額外的間接級別,但將獲得靈活性
class SampleClass { // a business class that will need to work with util capable of being changed during the runtime
@Autowired
private DynamicUtil util;
...
}
uj5u.com熱心網友回復:
您可以嘗試使用委托代理的方法。有一個主Utilbean,它只是對實際實作的包裝,并允許在運行時更改其內部委托。此外,您可以創建類似 manager/helper 類的東西,它包含對所有實際實作 bean 的參考,以簡化它們之間的切換。
@Component
@Primary
public class DelegatingUtil implements Util {
private Util delegate;
public void setDelegate(Util delegate){ this.delegate = delegate; }
public Util getDelegate(){ return delegate; }
public void getClient() {
return delegate.getClient();
}
}
以及適用于切換邏輯的地方:
// Use @Named or @Qualifier or any other way to obtain references to actual implementations
private Util defaultImpl;
private Util fallbackImpl;
@Autowired
private DelegatingUtil switcher;
public void switchToFallback(){
this.switcher.setDelegate(this.fallbackImpl);
}
請注意,這只是示意性示例,您應該注意諸如 bean 創建順序、使用限定符注入(可能是有條件的)、初始化等細節。
uj5u.com熱心網友回復:
這是根據您的情況的簡單方法。active.util主要思想是從 DB中讀取屬性PropertyService并將 Utils 包裝到RouteUtil:
@Component
public class RouteUtil {
@Autowired
private PropertyService propertyService;
@Qualifier("one")
@Autowired
private Util utilOne;
@Qualifier("two")
@Autowired
private Util utilTwo;
public void getClient() {
if ("one".equals(propertyService.read("active.util"))) {
utilOne.getClient();
} else {
utilTwo.getClient();
}
}
}
在演示服務中:
@Service
public class DemoService {
@Autowired
private RouteUtil util;
// RouteUtil.getClient() ...
}
您可以更改active.util以選擇將在運行時使用的 Util,而無需重新啟動應用程式。
uj5u.com熱心網友回復:
Spring 為您提供了一個我個人不喜歡的解決方案。你可以做的是宣告一個
@MyInterface
List<MyIntercase> myimpls
在哪里MyInterface是您的界面,串列將包含所有實作。但是,我(因為我不喜歡這個解決方案)撰寫了自己的解決方案,您可以在其中擁有一個由所有實作自行填充的靜態工廠。因此,您不必注入所有實作,而是在運行時通過類名或自定義名稱從工廠中選擇它們。另一個優點是每個工廠的自定義名稱必須是唯一的。因此,假設您有一些分階段的流程,并且對于每個階段,您都有自己的界面和自己的工廠。因此,您可以為不同介面的實作使用相同的自定義名稱。假設您使用文本格式 XML、JSON 和 CSV,并且有一個介面(和相關工廠)用于表示 stage-1 stage-2 stage-3。因此,對于每個 stage-X 介面,您可以將實作命名為JSON,XML所以CSV你所要做的就是有一個名為的變數currentType,它將保存其中一個值 - JSON,XML并且CSV對于每個階段,你都可以使用工廠來獲得適當的實作:
Stage1Handler handler = stage-1-factory.getInstance(currentValue);
Stage2Handler handler = stage-2-factory.getInstance(currentValue);
Stage3Handler handler = stage-3-factory.getInstance(currentValue);
Stage[X]Handler你的界面在哪里。但這只是一個額外的好處。我的解決方案在開源 MgntUtils 庫中可用。關于這個特殊功能的文章可以在這里找到:Non-intrusive access to "Orphaned" Beans in Spring framework另外,我在我的庫 javadoc 中描述了這個特性。該庫可以作為Maven 工件在Github上找到,包括源代碼和 Javadoc
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/463738.html
