我认为您不需要服务定位器模式,在现代 Spring 驱动的应用程序中通常不再需要它。
我将尝试从 Spring 框架的集成角度解决您的所有陈述:
我的应用程序中可能有多个 ClassA 对象,在运行时按需创建。
Spring 是一个运行时框架,所以一切都是在运行时创建的。如果您需要许多由需求创建的对象,您可以将 ClassA 声明为具有范围原型的 spring bean。
其他 bean 可以注入这个原型 bean。如果您知道在应用程序启动期间将创建哪些实例,另一种可能的方法是定义许多相同类型的 bean,并在注入期间使用 spring 的限定符功能来区分它们。
但是它们都应该使用相同的 InterfaceB 类的具体实现(InterfaceB 有几种实现,但根据所使用的平台,在运行时只使用一种)。
这意味着InterfaceB 可以是一个普通的单例,但是,给定不同的实现,您可以这样定义:
@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 的 cmets:
下面是工作示例:
在 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 的主类(在您的情况下,它应该可能是控制器或任何其他业务流):
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 中。