如何基于另一个bean的属性注入bean?

时间:2016-08-26 19:12:30

标签: java spring java-8 strategy-pattern

我正在尝试使用Spring来应用<input type="file" name="file" id="fileupload" (change)="onChange($event)"/> 模式(我认为我弄错了)

我的主要课程看起来像

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

有2个实现

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

@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

有2个实现

public interface DirectoryUserDataSource {
    DataSourceType getType();
}

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 DataSourceActiveDirectory的内容,具体的抓取工具应调用CsvActiveDirectoryUsersFetcher

我在哪里错过了理解?

提前致谢

1 个答案:

答案 0 :(得分:4)

所以,你有两个豆子,有相同的界面,但有两个不同的名字,但你已经知道了。现在你有多种选择......

你可以像@ crm86建议的那样,简单地将@Qualifie r添加到@Autowired,给出bean的名称。当然,这会将您的实现紧密地耦合到您使用它的地方,除了更改代码之外,您将无法使用其他实现。您也可以自动装配接口而不是实现类,但当然,出于同样的原因,这也是一个坏主意 - 为什么在首先使用依赖注入然后删除容器的任何选择?

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

@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并查看所有现有的获取者以找到合适的获取者,这样,您的工厂甚至不需要知道实际的实现但是可以在运行时发现它们......但我猜你得到的基本想法......