繁体   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