2011-03-27 4 views
6

요 전날 java.util.ServiceLoader을 사용하여 몇 가지 불편을 겪었으며 몇 가지 질문이 형성되었습니다. 내가 명시 적으로 특정 제네릭 형식으로 구현을로드 ServiceLoader을 말할 수java.util.ServiceLoader를 통해 일반 서비스 구현로드

public interface Service<T> { ... } 

:

내가 일반적인 서비스를 가정하자.

ServiceLoader<Service<String>> services = 
    ServiceLoader.load(Service.class); // Fail. 

내 질문은 : 안전하게 일반 서비스의 구현을로드 할 수 ServiceLoader를 사용하는 합리적인 방법은 무엇입니까?


위의 질문을 한 후와 파울로의 대답하기 전에 나는 해결책을 마련하기 위해 관리했습니다.

public interface Service<T> { ... 
    // true if an implementation can handle the given `t' type; false otherwise. 
    public boolean canHandle(Class<?> t) { ... 

public final class StringService implements Service<String> { ... 
    @Override public boolean canHandle(Class<?> t) { 
     if (String.class.isAssignableFrom(type)) 
      return true; 
     return false; 
    } 

public final class DoubleService implements Service<Double> { ... 
    // ... 

public final class Services { ... 
    public static <T> Service<T> getService(Class<?> t) { 
     for (Service<T> s : ServiceLoader.load(Service.class)) 
      if (s.canServe(t)) 
       return s; 
     throw new UnsupportedOperationException("No servings today my son!"); 
    } 

(내가 처음에 내 인터페이스 방법 boolean canHandle(T t) 있었다 나는 자신을 위해 후자를 사용하고 있습니다.) boolean canServe(Object o)-boolean canServe(Class<?> t)을 변경하고 같은 방식으로 <T> Service<T> getService(Class<?> t)을 변경하면 더 역동적이 될 수

답변

6

여기서 문제는 서비스 로더가 지정된 클래스/인터페이스의 모든 구현을 나열하는 파일을 사용한다는 것입니다. 파일의 이름은 인터페이스 이름으로 지정됩니다. 이 파일 이름에 type 매개 변수를 넣는 것은 예견되지 않았고 generic 형식을 Class 개체로 전달하는 것도 실제로 가능하지 않았습니다.

여기에서는 모든 유형의 일반 서비스 만 가져올 수 있으며 클래스 객체를 검사하여 하위 유형이 Service<String>인지 확인합니다. 이 같은

뭔가 :

class Test{ 

    public Service<String> getStringService() { 
     // it is a bit strange that we can't explicitely construct a 
     // parametrized type from raw type and parameters, so here 
     // we use this workaround. This may need a dummy method or 
     // variable if this method should have another return type. 
     ParametrizedType stringServiceType = 
      (ParametrizedType)Test.class.getMethod("getStringService").getGenericReturnType(); 

     ServiceLoader<Service<?>> loader = ServiceLoader.load(Service<?>.class); 
     for(Service<?> service : loader) { 
      if(isImplementing(service.getClass(), stringServiceType)) { 
       @SuppressWarnings("unchecked") 
       Service<String> s = (Service)service; 
       return s; 
      } 
     } 
    } 

    public boolean isImplementing(Class<?> candidate, ParametrizedType t) { 
     for(Type iFace : candidate.getGenericInterfaces()) { 
      if(iFace.equals(t)) { 
       return true; 
      } 
      if(iFace instanceof ParametrizedType && 
       ((ParametrizedType)iFace).getRawType().equals(t.getRawType())) { 
       return false; 
      } 
     } 
     return false; 
    } 

} 

이 테스트되지 않으며, 또한 우리의 (일반) 슈퍼 클래스에 의해 구현 우리의 클래스를 직접 구현하는 인터페이스에 의해 확장 된 인터페이스 및 인터페이스를 검색하도록 확장 될 필요가있다.

그리고 물론

, 이것은 단지 Example<String>는 서비스의 올바른 구현 될 수

class Example<X> implements Service<X> { ... } 

같은

class Example implements Service<String> { ...} 

하지 뭔가 같은 클래스를 찾을 수 있습니다.

+0

고마워요! 당신의 솔루션 *은 내가 제시 한 세부 사항을 고려하여 작동 할 수 있습니다. 그러나 나는 (아직) 그것을 테스트하지 않았다. 왜냐하면 내가 연구하고있는 주어진 프로젝트에 대해 * 우아한 * 솔루션을 제안했기 때문이다. 내 솔루션을 제공하기 위해 내 질문을 편집했습니다. 사이드 노트 : 이와 같이 구현하기에는 너무 복잡할까요? 'META-INF/services/com.example.Service' 여기서'com.example.서비스 '파일에는 다음과 같은 내용이 포함됩니다. = com.example.impl.StringService = com.example.impl.DoubleService 또는 좀 더 정교합니다. –

+0

너무 복잡하지는 않지만 직접해야합니다. (ServiceLoader가 사용하는 파일과 충돌을 일으키지 않기 위해 또 다른 디렉토리를 사용하라.) 나는 ServiceLoader 메카니즘을 1.5로 다시 구현했는데, 나중에 내 프로그램의 다른 부분에 1.6이 필요하다는 것을 발견했다. 그것을 버렸다. –

0

ServiceLoader 클래스 파일을 복사하고 load() 메서드에서 제네릭 형식 인수를 제거하여 항상 작동하게 할 수도 있습니다. 경고를 무시하면됩니다.

public static <S> ServiceLoader load(final Class<S> service) 
    { 
     return load(service, Thread.currentThread().getContextClassLoader()); 
    } 
관련 문제