2011-03-27 8 views
6

aracılığıyla genel hizmet uygulamalarını yükleme java.util.ServiceLoader'u kullanarak bazı aksaklıklar yaşadım ve bazı sorular bana oluştu. Ben açıkça belirli cins sadece uygulamaları yüklemek için ServiceLoader anlayamadıjava.util.ServiceLoader

public interface Service<T> { ... } 

:

Ben genel bir hizmet olduğunu varsayalım.

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

Sorum şu: güvenle jenerik hizmet uygulamalarını yüklemek için ServiceLoader kullanmak mantıklı yolları nelerdir?


Yukarıdaki soruyu sorduktan ve Paŭlo'nun cevabından önce bir çözüm bulmayı başardım.

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!"); 
    } 

(Başlangıçta benim arabirimde bir yöntemi boolean canHandle(T t) vardı kendim için ikincisi kullanıyorum.) boolean canServe(Object o) için boolean canServe(Class<?> t) değiştirilmesi ve aynı zamanda aynı şekilde <T> Service<T> getService(Class<?> t) değişen daha dinamik olabilir

cevap

6

Buradaki problem, servis yükleyicinin, verilen bir sınıf/arayüzün tüm uygulamalarını listeleyen bir dosya kullanmasıdır, bu dosya arayüzler ismiyle adlandırılmaktadır. Bu dosya ismine type parametresi koymak öngörüldü ve aynı zamanda jenerik türleri Class nesneler olarak geçirmek gerçekten mümkün değildir.

Burada, yalnızca herhangi bir türdeki genel hizmetlerinizi alabilir ve ardından sınıf nesnelerini, Service<String> alt türünün olup olmadığını kontrol edebilirsiniz. Böyle

şey:

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

} 

Bu test edilmemiştir ve aynı zamanda bizim (jenerik) üst sınıf tarafından uygulanan bizim sınıf doğrudan uygulayan arabirimler uzatıldı arayüzleri ve arayüzleri aramak için uzatılabilir gerekebilir.

Ve tabii

, bu sadece Example<String> hizmetinizin geçerli bir uygulama olabilir

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

gibi

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

değil bir şey gibi sınıflar bulabilirsiniz.

+0

Teşekkürler! Çözümünüz * sunduğum ayrıntıları göz önünde bulundurarak çalışabilir. Ancak henüz test etmedim, çünkü üzerinde çalıştığım proje için bir * zarif * çözüm buldum. Sorumu sunmak için sorumu düzenledim. Ayrıca, bir yan notta: Böyle bir şey uygulamak çok mu karmaşık olurdu? 'META-INF/services/com.example.Servis' 'com.example.Hizmet 'dosyası şu gibi bir şey içerir: ' = com.example.impl.StringService = com.example.impl.DoubleService' veya daha ayrıntılı bir şey. –

+0

Çok karmaşık değil, ancak bunu kendiniz yapmanız gerekiyor. ('ServiceLoader'ın kullandığı dosyalar ile çelişmemek için başka bir dizin kullanın.) Bir keresinde' ServiceLoader 'mekanizmasını 1,5 için kendim yeniden tasarladım, daha sonra programımın diğer bölümlerinin zaten 1,6'ya ihtiyaç duyduğunu buldum. ve onu attı. –

0

Ayrıca ServiceLoader sınıf dosyasını kopyalayabilir ve load() yönteminden jenerik tür bağımsız değişkenini kaldırarak her zaman çalışmasını sağlayabilirsiniz. Sadece uyarıları geçersiz kılmanız gerekecek.

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