Spring 没有自动装配的服务定位器

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对象(单例class)。

我无法自动装配 interfaceB,因为 ClassA 对象是在 ab 构造函数参数已知时在运行时创建的。

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

您可以创建一个额外的 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 框架的集成角度来解决您的所有陈述:

There could be several objects of ClassA in my application, created on demand at runtime.

Spring 是一个运行时框架,所以无论如何都是在运行时创建的。如果您需要按需创建许多对象,您可以将 ClassA 声明为具有范围原型的 spring bean。 其他 bean 可以注入这个原型 bean。如果您知道在应用程序启动期间将创建哪些实例,另一种可能的方法是定义许多相同类型的 bean,并使用 spring 的限定符功能在注入期间区分它们。

But all of them should use the same concrete implementation of InterfaceB class (there are several implementations of InterfaceB but only one is used at runtime depending on the platform used).

这意味着 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();
    }
}

I can not autowired interfaceB as ClassA objects are created at runtime when a and b constructor parameters are known.

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

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


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(在您的情况下,它应该可能是控制器或任何其他业务流程):

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 中。