복붙노트

[SPRING] 런타임“qualifier”변수를 사용하여 서비스를 동적으로 주입하는 방법은 무엇입니까?

SPRING

런타임“qualifier”변수를 사용하여 서비스를 동적으로 주입하는 방법은 무엇입니까?

런타임 값이 주어진 구성 요소 / 서비스를 주입하는 간단한 방법을 찾을 수 없습니다.

@ Spring의 문서를 읽기 시작했습니다 : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-autowired-annotation-qualifiers 그러나 @Qualifier 주석에 전달 된 값을 variabilize하는 방법을 찾을 수 없습니다.

그런 인터페이스를 가진 모델 엔터티가 있다고 가정 해 봅시다.

public interface Case {

    String getCountryCode();
    void setCountryCode(String countryCode);

}

내 클라이언트 코드에서 다음과 같은 작업을 수행합니다.

@Inject
DoService does;

(...)

Case myCase = new CaseImpl(); // ...or whatever
myCase.setCountryCode("uk");

does.whateverWith(myCase);

... 내 서비스는 다음과 같습니다.

@Service
public class DoService {

    @Inject
    // FIXME what kind of #$@& symbol can I use here?
    // Seems like SpEL is sadly invalid here :(
    @Qualifier("${caze.countryCode}")
    private CaseService caseService;

    public void whateverWith(Case caze) {
        caseService.modify(caze);
    }

}

caseService가 UKCaseService가 될 것으로 예상합니다 (아래 관련 코드 참조).

public interface CaseService {

    void modify(Case caze);

}

@Service
@Qualifier("uk")
public class UKCaseService implements CaseService {

}

@Service
@Qualifier("us")
public class USCaseService implements CaseService {

}

따라서 스프링 기능 중 하나를 사용하여 가장 간단하고 우아하고 효율적인 방식 으로이 모든 것을 어떻게 "고정"합니까? 본질적으로 NO .properties, NO XML, 주석 만 있습니다. 그러나 Spring은 caseService를 주입하기 전에 "case"를 알아야하기 때문에 DoService에서 무언가 잘못되었다고 생각합니다 ...하지만 클라이언트 코드가 caseService에 대해 알지 못하고 이것을 달성하는 방법은 무엇입니까?! 나는 이것을 알아낼 수 없다 ...

나는 이미 여기에 몇 가지 문제를 읽었지만 대부분의 경우 실제로는 내가 필요로하는 것과 동일한 요구 및 / 또는 구성을 가지고 있지 않거나 게시 된 답변이 나에게 만족스럽지 않습니다 (필수 사항처럼 보입니다) 해결 방법 또는 (이전) Spring 기능의 (이전) 사용법).

일치하는 Bean이 둘 이상있을 때 Spring은 이름으로 어떻게 자동 와이어 링합니까? =>는 컴포넌트와 유사한 클래스만을 참조합니다

스프링에서 자동으로 연결할 Bean을 동적으로 정의 (자격자 사용) => 정말로 흥미롭지 만 가장 정교하게 대답 된 답변 (4 투표)은 ... 거의 3 1/2 세입니다! (2013 7 월)

Spring 3-다른 객체 속성을 기반으로 런타임에 동적 자동 배선 => 여기에서 매우 비슷한 문제이지만 대답은 실제로 실제 디자인 패턴 (공장과 같은)과 같은 해결책입니까? 그리고 모든 코드를 ServiceImpl에 구현하는 것을 좋아하지 않습니다 ...

Spring @Autowiring, 객체 팩토리를 사용하여 구현을 선택하는 방법은 무엇입니까? => 두 번째 대답은 흥미롭게 보이지만 그 저자는 확장되지 않으므로 Java Config & stuff에 대해 조금 알지만, 그가 무엇을 이야기하고 있는지 잘 모르겠습니다 ...

XML이없는 Spring의 속성을 기반으로 런타임에 다른 서비스를 주입하는 방법 => 흥미로운 토론, 특히 대답은 있지만 사용자에게는 속성이 설정되어 있지만 내가 가지고 있지 않습니다.

또한 이것을 읽으십시오 : http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-bean-references => 식에서 "@"사용에 대한 확장 된 예를 찾을 수 없습니다. 누군가 이것에 대해 알고 있습니까?

편집하다: 다른 유사한 문제를 발견했지만 아무도 정답을 얻지 못했습니다. @Autowired를 사용하여 팩토리 패턴과 같은 구현을 동적으로 주입하는 방법 스프링 한정자 및 속성 자리 표시 자 스프링 : 속성 자리 표시 자와 함께 @Qualifier 사용 봄에 조건부 자동 배선을 수행하는 방법은 무엇입니까? 스프링의 동적 주입 @Qualifier의 SpEL은 동일한 Bean을 참조합니다. SpEL을 사용하여 Spring에서 메소드 호출 결과를 주입하는 방법은 무엇입니까?

팩토리 패턴이 해결책일까요? @Autowired를 사용하여 팩토리 패턴과 같은 구현을 동적으로 주입하는 방법

해결법

  1. ==============================

    1.BeanFactory를 사용하여 이름으로 컨텍스트에서 Bean을 동적으로 얻을 수 있습니다.

    BeanFactory를 사용하여 이름으로 컨텍스트에서 Bean을 동적으로 얻을 수 있습니다.

    @Service
    public class Doer {
    
      @Autowired BeanFactory beans;
    
      public void doSomething(Case case){
        CaseService service = beans.getBean(case.getCountryCode(), CaseService.class)
        service.doSomething(case);
      }
    }
    

    참고 사항. 국가 이름과 같은 것을 빈 이름으로 사용하는 것은 약간 이상하게 보입니다. 접두사를 적어도 추가하거나 다른 디자인 패턴을 고려하는 것이 좋습니다.

    국가별로 콩을 갖고 싶다면 다른 접근법을 제안합니다. 국가 코드별로 필요한 서비스를 받으려면 레지스트리 서비스를 도입하십시오.

    @Service
    public class CaseServices {
    
      private final Map<String, CaseService> servicesByCountryCode = new HashMap<>();
    
      @Autowired
      public Cases(List<CaseService> services){
        for (CaseService service: services){
          register(service.getCountryCode(), service);
        }
      }
    
      public void register(String countryCode, CaseService service) {
        this.servicesByCountryCode.put(countryCode, service);
      }
    
      public CaseService getCaseService(String countryCode){
        return this.servicesByCountryCode.get(countryCode);
      }
    }
    

    사용법 예 :

    @Service
    public class DoService {
    
      @Autowired CaseServices caseServices;
    
      public void doSomethingWith(Case case){
        CaseService service = caseServices.getCaseService(case.getCountryCode());
        service.modify(case);
      }
    }
    

    이 경우 CaseService 인터페이스에 String getCountryCode () 메소드를 추가해야합니다.

    public interface CaseService {
        void modify(Case case);
        String getCountryCode();
    }
    

    또는 CaseService.supports (Case case) 메소드를 추가하여 서비스를 선택할 수 있습니다. 또는 인터페이스를 확장 할 수없는 경우 일부 초 기자 또는 @Configuration 클래스에서 CaseServices.register (String, CaseService) 메소드를 호출 할 수 있습니다.

    업데이트 : Spring은 이미 PluginRegistry를 생성하기 위해 상용구 코드를 재사용하기 위해 멋진 플러그인 추상화를 제공한다는 것을 언급하지 않았습니다.

    예:

    public interface CaseService extends Plugin<String>{
        void doSomething(Case case);
    }
    
    @Service
    @Priority(0)
    public class SwissCaseService implements CaseService {
    
      void doSomething(Case case){
        // Do something with the Swiss case
      }
    
      boolean supports(String countryCode){
        return countryCode.equals("CH");
      }
    }
    
    @Service
    @Priority(Ordered.LOWEST_PRECEDENCE)
    public class DefaultCaseService implements CaseService {
    
      void doSomething(Case case){
        // Do something with the case by-default
      }
    
      boolean supports(String countryCode){
        return true;
      }
    }
    
    @Service
    public class CaseServices {
    
      private final PluginRegistry<CaseService<?>, String> registry;
    
      @Autowired
      public Cases(List<CaseService> services){
        this.registry = OrderAwarePluginRegistry.create(services);
      }
    
      public CaseService getCaseService(String countryCode){
        return registry.getPluginFor(countryCode);
      }
    }
    
  2. ==============================

    2.이 SO 답변에 따르면 @Qualifier를 사용하면 많은 도움이되지 않습니다 : 한정자로 ApplicationContext에서 Bean 가져 오기

    이 SO 답변에 따르면 @Qualifier를 사용하면 많은 도움이되지 않습니다 : 한정자로 ApplicationContext에서 Bean 가져 오기

    대체 전략에 관해서는 :

    유스 케이스는 애플리케이션 시작시 Bean이 작성되는 시나리오를 중심으로 돌아가지만 applicationContext가 Bean 주입을 완료 한 후에 선택한 Bean을 해결해야합니다.

  3. from https://stackoverflow.com/questions/42909283/how-to-dynamically-inject-a-service-using-a-runtime-qualifier-variable by cc-by-sa and MIT license