簡體   English   中英

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 object(單例類)。

ab構造函數參數已知時,我無法自動裝配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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM