2016-08-26 7 views
0

나는 (내가 그것을 잘못 생각하는) 스프링을 사용하여 Strategy 패턴을 적용하려고다른 bean의 속성을 기반으로 bean을 주입하는 방법은 무엇입니까? 다음과 같은

DirectoryUsersFetcher

public interface DirectoryUsersFetcher { 
    Iterator<String> importUsers(DirectoryUserDataSource dataSource); 
} 
같은 인터페이스입니다
@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)); 
    } 
} 

처럼 내 주 클래스 본다

(구현이 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 구현으로

,210

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

test 내가이 문제를 해결할 수있는 방법

 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 

입니다 참조 무엇

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

처럼 보인다?

내가 필요한 것?DataSourceActiveDirectory 또는 Csv 무엇을 기반으로

, 특정 가져 오기는 ActiveDirectoryUsersFetcher 호출해야 또는 CsvUsersFetcher

어디 이해를 놓친 거지? 사전 그래서

+0

왜 fetcher에 첫 번째 장소에서 데이터 소스? – zeroflagL

답변

4

에서

덕분에, 당신은 동일한 인터페이스 두 콩,하지만 두 개의 서로 다른 이름을 가지고,하지만 당신은 이미 알고있다. 이제 여러 선택 항목이 있습니다.

@ crm86에서 제안한대로 간단히 @Qualifie r을 @Autowired에 추가하여 콩의 이름을 지정할 수 있습니다. 물론 코드를 변경하는 것 이외에는 구현을 코드를 사용하는 곳에 단단히 연결하면 다른 코드를 사용할 방법이 없습니다. 인터페이스는 아니지만 구현 클래스도 autowire 할 수 있지만, 물론 이것은 동일한 이유로 나쁜 아이디어이기도합니다. 왜 컨테이너에 대한 선택 사항을 제거 할 때 종속성 삽입을 처음 사용하려고합니까?

또 다른 방법은 @Configuration과 같은 방법을 사용하여 첫 번째 장소에 DirectoryUsersFetcher의 인스턴스를 하나만 만드는 것입니다.(당신이 그 (것)들에게 프로토 타입을 선언하지 않는 경우 - 그러나 이것은 내가 귀찮은 생각하는 주위 어딘가에 유형을 유지하기 위해 당신이 필요합니다)

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

는 물론, 이것은 런타임 당 하나의 가져 오기에 응용 프로그램을 제한합니다. 어쨌든 형식을 정의해야합니다.

또 다른 방법은 직접 콩을 만들 수 있지만 사용하는 공장 패턴을, 예를 들면 ...

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

} 

이 방법을 직접 콩 대신 공장을 연결할 수 없습니다하는 것입니다. 팩토리는 객체 등을 캐시 할 수도 있습니다. 개인적으로, 나는 그 해결책을 제안 할 것입니다. 물론

@Autowired 
private DirectoryUsersFetcherFactory factory; 

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

, 단순히 공장의에는 Fetcher를 autowire하기 콩로에는 Fetcher를 반환 할 수 귀하의 공장 ...

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

} 

그래서 많은 선택 ;-) 당신은 또한에는 Fetcher를 연결할 수 데이터 소스로 ApplicationContext를 autowire하고 기존의 모든 fetcher를 살펴보고 적합한 방법을 찾으십시오.이 방법을 사용하면 실제 구현을 알 필요가 없지만 런타임에 발견 할 수 있습니다 ...하지만 기본 아이디어 ...

+0

여전히 혼란 스럽습니다. 두 콩을 모두 사용해야하는데'DataSourceType'이 무엇인지에 따라 선택해야합니다. – daydreamer

+1

공장 사용. 가져 오기 도구 자체 대신 와이어를 연결 한 다음 DataSourceType을 팩토리 메서드에 제공하고 메서드에서 적절한 fetcher를 만들거나 반환하도록합니다. –

+0

흠, 나를 찾아보고 이해할 수있는 예 또는 참고 자료로 안내 할 수 있습니까? – daydreamer

관련 문제