Java 弹簧维修定位器,无需自动接线

Java 弹簧维修定位器,无需自动接线,java,spring,spring-boot,dependency-injection,service-locator,Java,Spring,Spring Boot,Dependency Injection,Service Locator,有此代码: 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类的具体实现(有几

有此代码:

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
类的具体实现(有几种
InterfaceB
实现,但根据使用的平台,在运行时只使用一种)。应用程序中应该只有一个
InterfaceB
对象(singleton类)

a
b
构造函数参数已知时,我无法自动连接
interfaceB
,因为
ClassA
对象是在运行时创建的


我如何在这里使用Spring框架来实现服务定位器模式?我的计划是在
ClassA
中实例化服务定位器,并使用它获取
InterfaceB
对象

您可以创建一个额外的类,该类将创建您的
ClassA
,它将保存对
接口b
的引用。例如:

@Component
public class ClassAFactory {

    @Autowired
    private InterfaceB interfaceB;

    public ClassA create(int a, int b) {
       return new ClassA(a, b, interfaceB);
    }
}
在这种情况下,您必须扩展
ClassA
以传递
接口b
。 然后在代码中的某个地方,您可以:

@Autowired
private ClassAFactory factory ;

...

ClassA classA = factory.create(1,1); 


我认为您不需要服务定位器模式,在现代spring驱动的应用程序中,它通常不再需要

我将尝试从Spring框架的集成角度阐述您的所有陈述:

在我的应用程序中可能有几个ClassA对象,它们是在运行时按需创建的

Spring是一个运行时框架,所以一切都是在运行时创建的。如果您需要许多由demand创建的对象,您可以将ClassA声明为具有范围原型的Springbean。 其他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();
    }
}
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();
    }
}
我不能自动连接interfaceB,因为当a和b构造函数参数已知时,ClassA对象是在运行时创建的

事实上你可以,没问题。将classA的bean定义为原型

如果您只想在第一次调用时初始化该classA的实例,请检查spring的
@Lazy
注释


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>
在最后一个打印说明中,ClassA实例不同,但interfaceB是相同的共享实例


旁注:提供程序已经与spring集成,它驻留在新jar的
javax.inject.Provider
中。

如何使用此示例按需创建InterfaceB对象和ClassA对象?执行类似AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(MyConfig.class)然后执行context.getBean(“classA”)?请使用实际使用“classA”的对象的代码片段更新问题,以便我可以在您的案例中演示如何执行。基本上,您不应该在业务代码中直接使用应用程序上下文……您可以假设serviceX拥有类X对象的内存列表,并为它们执行CRUD。ClassX对象有一个属性是ClassA对象。在给定的时间内(例如,一些用户请求),服务被请求添加一个新的ClassX对象。ClassX对象如何获得工厂?ClassX对象的“新建”由ServiceX@MABC-请查看我答案的更新,我已经在我的机器上运行了此示例,它复制了您的案例并解决了问题。如果您需要其他信息,请告诉我。为什么不在ClassA构造函数中使用this.interfaceB=ctx.getBean(“interfaceB”)?如果不是由Spring自动连接,我将如何获得工厂?我的意思是,持有工厂引用的对象不是bean本身,所以你想在spring管理的代码之外创建
ClassA
?你能举例说明你想如何创建
ClassA
?你可以假设一个serviceX拥有一个ClassX对象的内存列表,并对它们执行CRUD。ClassX对象有一个属性是ClassA对象。在给定的时间内(例如,一些用户请求),服务被请求添加一个新的ClassX对象。ClassX对象如何获得工厂?ClassX对象的“新建”由ServiceXFactory执行,应该在serviceX中(例如使用@Autowired)。如果您想通过添加ClassA来更改ClassX,那么serviceX应该这样做:
ClassX.ClassA=factory.create(1,1)如果工厂位于serviceX中,为了将ClassA对象注入ClassB,它将serviceX与工厂和ClassA耦合,并不必要地公开ClassX的内部细节(违反信息隐藏原则)。我认为工厂应该在ClassA中,在ClassA的构造函数中,它使用工厂获取InterfaceB对象。如果我将工厂声明为带有@Component的Bean,那么我如何使用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();
    }
}
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();
    }