簡體   English   中英

如何基於另一個bean的屬性注入一個bean?

[英]How to inject a bean based on the property of another bean?

我正在嘗試使用Spring如下應用Strategy模式(我認為我理解錯了)

我的主班看起來像

@Component
public class DirectoryUserImportWorkflow {
    private List<DirectoryUserDataSource> dataSources = Arrays.asList(new ActiveDirectoryDataSource(), new CsvDataSource());

    @Autowired
    private DirectoryUsersFetcher directoryUsersFetcher;

    public void run() {
        dataSources.forEach(dataSource -> directoryUsersFetcher.importUsers(dataSource));
    }
}

其中DirectoryUsersFetcher是一個接口

public interface DirectoryUsersFetcher {
    Iterator<String> importUsers(DirectoryUserDataSource dataSource);
}

與2實現

@Component
public class ActiveDirectoryUsersFetcher implements DirectoryUsersFetcher {
    public Iterator<String> importUsers(DirectoryUserDataSource dataSource) {
        System.out.println("Returning data from Active Directory");
        return Arrays.asList("ActiveDirectoryUser1", "ActiveDirectoryUser2", "ActiveDirectoryUser3").iterator();
    }
}

@Component
public class CsvUsersFetcher implements DirectoryUsersFetcher {
    public Iterator<String> importUsers(DirectoryUserDataSource dataSource) {
        System.out.println("Returning data from CSV");
        return Arrays.asList("CsvUser1", "CsvUser2", "CsvUser3").iterator();
    }
}

我希望基於DataSourceType是在運行時使用它們之一

public enum DataSourceType {
    DirectoryServer,
    Csv
}

DataSource本身是一個界面,看起來像

public interface DirectoryUserDataSource {
    DataSourceType getType();
}

與2實現

public class ActiveDirectoryDataSource implements DirectoryUserDataSource {
    public DataSourceType getType() {
        return DataSourceType.DirectoryServer;
    }
}

public class CsvDataSource implements DirectoryUserDataSource {
    public DataSourceType getType() {
        return DataSourceType.Csv;
    }
}

我的test看起來像

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DirectoryUserImportWorkflow.class, ActiveDirectoryUsersFetcher.class, CsvUsersFetcher.class})
public class DirectoryUserImportWorkflowTest {

    @Autowired
    private DirectoryUserImportWorkflow workflow;

    @Test
    public void runStrategy() throws Exception {
        workflow.run();
    }
}

我看到的是

        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'directoryUserImportWorkflow': Unsatisfied dependency expressed through field 'directoryUsersFetcher': No qualifying bean of type [com.learner.datafetcher.DirectoryUsersFetcher] is defined: expected single matching bean but found 2: activeDirectoryUsersFetcher,csvUsersFetcher; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.learner.datafetcher.DirectoryUsersFetcher] is defined: expected single matching bean but found 2: activeDirectoryUsersFetcher,csvUsersFetcher

我怎么解決這個問題?

我需要的?

根據什么DataSourceActiveDirectoryCsv ,特定的提取程序應調用ActiveDirectoryUsersFetcherCsvUsersFetcher

我在哪里缺少了解?

提前致謝

因此,您有兩個具有相同接口但兩個名稱不同的bean,但是您已經知道了。 現在您有多種選擇...

您可以按照@ crm86的建議,簡單地將@Qualifie r添加到@Autowired ,以提供bean的名稱。 當然,這會將您的實現緊密地耦合到您使用它的地方,並且您將無法使用其他實現-除了更改代碼。 您也可以自動裝配接口而不是實現類,但是出於同樣的原因,這也是一個壞主意-為什么在刪除容器的任何選擇時首先嘗試使用依賴項注入?

另一種方法是首先使用@Configuration和類似...的方法來創建DirectoryUsersFetcher一個實例。

@Bean
public DirectoryUsersFetcher directoryUsersFetcher () {
// decide, create, return
}

當然,這會將您的應用程序每次運行時限制為僅一個訪存程序(如果您未聲明它們為原型-但這將需要您將類型保留在某個地方,這我覺得很麻煩)。 無論如何,您都需要在某個地方定義類型。

另一種方法是不直接創建bean,而是使用工廠模式,例如...

@Component
public class DirectoryUsersFetcherFactory {

    public DirectoryUsersFetcher createDirectoryUsersFetcher (ActiveDirectoryDataSource dataSource) {
          DataSourceType type = dataSource.getType();
          if(type == DataSourceType.DirectoryServer) 
              return new ActiveDirectoryUsersFetcher ();
          if(type == DataSourceType.Csv) 
              return new CsvUsersFetcher ();
          throw new IllegalArgumentException("Unknown type" + type);
    } 

}

這樣,您可以直接為工廠接線,而不是直接為bean接線。 工廠還可以緩存對象等。就我個人而言,我建議該解決方案。

@Autowired
private DirectoryUsersFetcherFactory factory;

public void run() {
    dataSources.forEach(dataSource -> directoryUsersFetcher.importUsers(factory.createDirectoryUsersFetcher(dataSource)));
}

當然,您的工廠也可以簡單地在工廠中自動為取貨機布線,並將取貨機作為豆類退回。

@Component
public class DirectoryUsersFetcherFactory {

    @Autowired
    private ActiveDirectoryUsersFetcher activeDirectoryUsersFetcher ;

    @Autowired
    private CsvUsersFetcher csvUsersFetcher ;

    public DirectoryUsersFetcher createDirectoryUsersFetcher (ActiveDirectoryDataSource dataSource) {
          DataSourceType type = dataSource.getType();
          if(type == DataSourceType.DirectoryServer) 
              return activeDirectoryUsersFetcher ;
          if(type == DataSourceType.Csv) 
              return csvUsersFetcher;
          throw new IllegalArgumentException("Unknown type" + type);
    } 

}

如此之多的選擇;-)您甚至還可以將提取程序與數據源相關聯,自動連接ApplicationContext並查看所有現有的提取程序以找到合適的提取程序,這樣,您的工廠甚至不需要知道實際的實現,而是可以發現它們在運行時...但是我想您已經了解了基本概念...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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