Я не думаю, что вам нужен шаблон локатора сервисов, в современных приложениях, управляемых Spring, он обычно больше не требуется.
Я постараюсь рассмотреть все ваши утверждения с точки зрения интеграции Spring Framework:
В моем приложении может быть несколько объектов ClassA, создаваемых по запросу во время выполнения.
Spring - это среда выполнения, поэтому все в любом случае создается во время выполнения. Если вам нужно много объектов, созданных по запросу, вы можете объявить ClassA как Spring 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();
}
}
Я не могу автоматически подключить интерфейсB, поскольку объекты ClassA создаются во время выполнения, когда известны параметры конструктора a и b.
Вообще-то можно, с этим проблем нет. Определите bean-компонент classA как прототип.
Проверьте также @Lazy
аннотацию spring на случай, если вы хотите инициализировать экземпляр этого 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 из контекста приложения (в вашем случае это должен быть, вероятно, контроллер или любой другой бизнес-поток):
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 - это тот же общий экземпляр.
Примечание: поставщик уже интегрирован с Spring, он находится в javax.inject.Provider
этой новой банке.