gpt4 book ai didi

java - 如何根据另一个bean的属性注入(inject)一个bean?

转载 作者:行者123 更新时间:2023-12-01 18:03:05 25 4
gpt4 key购买 nike

我正在尝试使用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是一个接口(interface)

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本身是一个接口(interface),看起来像

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;
}
}

我的测试看起来像

@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,特定的提取器应调用ActiveDirectoryUsersFetcher CsvUsersFetcher

我在哪里缺少理解?

提前致谢

最佳答案

所以,您有两个 bean,具有相同的接口(interface)但有两个不同的名称,但您已经知道了。现在你有多种选择...

您可以按照@crm86的建议,简单地将@Qualifier添加到@Autowired,并给出bean的名称。当然,这会将您的实现紧密地耦合到您使用它的地方,并且您将无法使用另一个实现 - 除了更改代码。您也可以不 Autowiring 接口(interface),而是 Autowiring 实现类,但是,当然,出于同样的原因,这也是一个坏主意 - 为什么在删除容器的任何选择时首先尝试使用依赖项注入(inject)?

另一种方法是首先使用 @Configuration 和类似的方法创建 DirectoryUsersFetcher 的一个实例...

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

当然,这将限制您的应用程序每个运行时只能使用一个 getter (如果您不声明它们原型(prototype) - 但这将要求您将类型保留在某个地方,我认为这很麻烦)。无论如何,您都需要在某处定义类型。

另一种方法是不直接创建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)));
}

当然,您的工厂也可以简单地 Autowiring 工厂中的 getter 并将 getter 作为bean返回...

@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);
}

}

如此多的选择;-)您甚至还可以将提取器与数据源相关联, Autowiring 您的 ApplicationContext 并查看所有现有的提取器以找到合适的提取器,这样,您的工厂甚至不需要知道实际的实现但可以在运行时发现它们...但我想你已经了解了基本的想法...

关于java - 如何根据另一个bean的属性注入(inject)一个bean?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39173266/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com