[英]Spring service locator without autowiring it
有這個代碼:
public class ClassA {
private InterfaceB interfaceB;
private int a
private int b;
public ClassA(int a, int b) {
this.a = a;
this.b = b;
}
}
我的應用程序中可能有多個ClassA
對象,它們是在運行時按需創建的。 但是它們都應該使用相同的InterfaceB
class 的具體實現( InterfaceB
有幾種實現,但根據所使用的平台,只有一種在運行時使用)。 並且應用程序中應該只有一個InterfaceB
object(單例類)。
當a
和b
構造函數參數已知時,我無法自動裝配interfaceB
,因為ClassA
對象是在運行時創建的。
我怎么能在這里使用 Spring 框架來實現服務定位器模式? 我的計划是在ClassA
中實例化服務定位器並使用它來獲取InterfaceB
object。
您可以創建一個額外的 class 來創建您的ClassA
並且它將持有對interfaceB
的引用。 例如:
@Component
public class ClassAFactory {
@Autowired
private InterfaceB interfaceB;
public ClassA create(int a, int b) {
return new ClassA(a, b, interfaceB);
}
}
在這種情況下,您必須擴展ClassA
以傳遞interfaceB
。 然后在你的代碼中的某個地方你可以:
@Autowired
private ClassAFactory factory ;
...
ClassA classA = factory.create(1,1);
我認為您不需要服務定位器模式,在現代 spring 驅動的應用程序中,它通常不再需要。
我將嘗試從 Spring 框架的集成角度解決您的所有陳述:
我的應用程序中可能有多個 ClassA 對象,它們是在運行時按需創建的。
Spring 是一個運行時框架,所以一切都是在運行時創建的。 如果您需要許多由需求創建的對象,您可以將 ClassA 聲明為 spring bean 和 scope 原型。 其他 bean 可以注入這個原型 bean。 如果您知道在應用程序啟動期間將創建哪些實例,另一種可能的方法是定義許多相同類型的 bean,並在注入期間使用 spring 的限定符功能來區分它們。
但是它們都應該使用相同的 InterfaceB class 的具體實現(InterfaceB 有幾種實現,但根據所使用的平台,只有一種在運行時使用)。
這意味着InterfaceB
可以是常規的 singleton,但是,給定不同的實現,您可以定義如下內容:
@Configuration
public class MyConfiguration {
@Bean
@ConditionalOnProperty(name="myprop", havingValue="true")
public InterfaceB interfaceBImpl1() {
return new InterfaceBImpl1();
}
@Bean
@ConditionalOnProperty(name="myprop", havingValue="false")
public InterfaceB interfaceBImpl2() {
return new InterfaceBImpl2();
}
}
當 a 和 b 構造函數參數已知時,我無法自動裝配 interfaceB,因為 ClassA 對象是在運行時創建的。
其實可以,沒有問題。 將classA的bean定義為原型。
還要檢查 spring 的@Lazy
注釋,以防您只想在第一次調用時初始化該 classA 的實例。
public class ClassA {
/// fields ///
public ClassA(InterfaceB intefaceB, int a, int b) {
this.intefaceB = intefaceB;
this.a = a;
this.b = b;
}
}
@Configuration
class MyConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ClassA classA(InterfaceB bImpl, int a, int b) {
return new ClassA(bImpl, int a, int b);
}
}
更新 1
根據OP的評論:
這是工作示例:
在 pom.xml 中添加以下依賴項:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
它只包含接口,沒有傳遞依賴
然后根據您在評論中解釋的用例:
public class InterfaceB {
}
public class ClassA {
private final InterfaceB interfaceB;
public ClassA(InterfaceB interfaceB) {
this.interfaceB = interfaceB;
}
public void doSomething() {
System.out.println("Doing something on instance: [ " + this + " ]. The interface B instance is [ "+ interfaceB + " ]");
}
}
public class ServiceA {
private final List<ClassA> classAList;
private Provider<ClassA> classAProvider;
public ServiceA(Provider<ClassA> classAProvider) {
this.classAProvider = classAProvider;
this.classAList = new ArrayList<>();
}
public void addNewObject() {
ClassA newObj = classAProvider.get();
classAList.add(newObj);
}
public void doWithAllElementsInList() {
classAList.forEach(ClassA::doSomething);
}
}
Spring 配置如下所示:
public class SingletonWithPrototypesConfig {
@Bean
public ServiceA serviceA(Provider<ClassA> classAProvider) {
return new ServiceA(classAProvider);
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ClassA classA(InterfaceB interfaceB) {
return new ClassA(interfaceB);
}
@Bean
public InterfaceB interfaceB() {
return new InterfaceB();
}
}
以及從應用程序上下文獲取服務 A 的主要 class (在您的情況下,它可能應該是 controller 或任何其他業務流程):
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SingletonWithPrototypesConfig.class);
ServiceA serviceA = ctx.getBean(ServiceA.class);
serviceA.doWithAllElementsInList(); // won't print anything, 0 elements in the list
System.out.println("---------");
serviceA.addNewObject();
serviceA.addNewObject();
serviceA.doWithAllElementsInList();
}
在最后的打印說明中 ClassA 實例不同,但 interfaceB 是相同的共享實例。
旁注:Provider 已經與 spring 集成,它位於新 jar 的javax.inject.Provider
中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.