[英]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.