2009-07-17 19 views
4

Je me réfère à "framework fournisseur de services" comme discuté dans Chapter 2 of Effective Java, qui semble être exactement la bonne façon de gérer un problème que j'ai, où je dois instancier une de plusieurs classes à l'exécution, basé sur un String pour sélectionner quel service , et un Configuration objet (essentiellement un extrait XML):Simple Java "Service Provider frameworks"?

Mais comment obtenir les fournisseurs de services individuels (par exemple un groupe de fournisseurs par défaut + certains fournisseurs personnalisés) pour s'enregistrer?

interface FooAlgorithm 
{ 
    /* methods particular to this class of algorithms */ 
} 

interface FooAlgorithmProvider 
{ 
    public FooAlgorithm getAlgorithm(Configuration c); 
} 

class FooAlgorithmRegistry 
{ 
    private FooAlgorithmRegistry() {} 
    static private final Map<String, FooAlgorithmProvider> directory = 
     new HashMap<String, FooAlgorithmProvider>(); 
    static public FooAlgorithmProvider getProvider(String name) 
    { 
     return directory.get(serviceName); 
    } 
    static public boolean registerProvider(String name, 
     FooAlgorithmProvider provider) 
    { 
     if (directory.containsKey(name)) 
      return false; 
     directory.put(name, provider); 
     return true; 
    } 
} 

par exemple. Si j'écris des classes personnalisées MyFooAlgorithm et MyFooAlgorithmProvider pour implémenter FooAlgorithm et que je les distribue dans un fichier jar, existe-t-il un moyen d'appeler automatiquement registerProvider ou mes programmes clients qui utilisent l'algorithme doivent-ils appeler explicitement FooAlgorithmRegistry.registerProvider() pour chaque classe qu'ils veulent utiliser?

Répondre

10

Je pense que vous devez créer un META-INF/services/fully.qualified.ClassName et énumérer des choses là-bas, mais je ne me souviens pas de la spécification (JAR File Specification ou this).

Le confessions de conception API pratique d'un architecte Java livre chapitre 8 est sur SPI.

Le ServiceLoader peut vous aider à répertorier les implémentations disponibles. Par exemple avec l'interface PersistenceProvider:

ServiceLoader<PersistenceProvider> loader = 
     ServiceLoader.load(PersistenceProvider.class); 
Iterator<PersistenceProvider> implementations = loader.iterator(); 
while(implementations.hasNext()) { 
    PersistenceProvider implementation = implementations.next(); 
    logger.info("PersistenceProvider implementation: " + implementation); 
} 
+2

comme référence: ceci nécessite Java6 – dfa

+1

+1 pour la référence du livre, merci –

+2

intéressant: la réponse semble donc être qu'il existe un mécanisme de fichier JAR pour inclure les fichiers de configuration du fournisseur de service, + diverses classes/méthodes pour les instancier (par ex. java.util.ServiceLoader) –

0

Vous pourriez avoir le JAR client enregistrent les fournisseurs dans un bloc d'initialisation statique dans une classe que vous savez être appelé avant FooAlgorithmRegistry.getProvider(), quelque chose comme:

static { 
    FooAlgorithmRegistry.registerProvider("test", new MyFooAlgorithmProvider()); 
} 

Mais, il peut être assez difficile de trouver un moyen de garantir que cela fonctionnera (les initialiseurs statiques sont garantis être exécutés une fois et une seule fois, lorsque la classe est chargée pour la première fois) avant la méthode accessor de l'usine.

+0

Mais vous devez référencer explicitement votre classe quelque part pour l'initialiser. Alternativement, vous pouvez trouver votre propre fichier jar à l'exécution et le parcourir pour trouver des classes avec \ * Provider dans le nom et Class.forName it. Laid. – akarnokd