Personnellement, je n'aime pas l' ServiceLoader
, en toutes circonstances. C'est lent et inutilement coûteux et il ya peu que vous pouvez faire pour l'optimiser.
Je trouve aussi que c'est un peu limité -- vous avez vraiment besoin de sortir de votre chemin si vous voulez faire plus de recherche par type seul.
xbean-finder ResourceFinder
-
ResourceFinder est lui-même un fichier java capable de remplacer le ServiceLoader d'utilisation. Copier/coller la réutilisation n'est pas un problème. C'est un fichier java et est l'ASL 2.0 licence et est disponible à partir de Apache.
Avant notre attention trop court, voici comment il est possible de remplacer une ServiceLoader
ResourceFinder finder = new ResourceFinder("META-INF/services/");
List<Class<? extends Plugin>> impls = finder.findAllImplementations(Plugin.class);
Cela permettra de trouver toutes les de la META-INF/services/org.acme.Plugin
des implémentations dans votre classpath.
Remarque il ne fait pas instancier toutes les instances. De choisir celle(s) que vous voulez et vous êtes un newInstance()
appel loin de disposer d'une instance.
Pourquoi est-ce agréable?
- Comment est-il difficile d'appeler
newInstance()
avec une bonne gestion des exceptions? Pas dur.
- Avoir la liberté d'instancier seulement ceux que vous voulez, c'est agréable.
- Maintenant vous pouvez soutenir constructeur args!
Le rétrécissement de la zone de recherche
Si vous voulez juste vérifier les URLs, vous pouvez le faire facilement:
URL url = new File("some.jar").toURI().toURL();
ResourceFinder finder = new ResourceFinder("META-INF/services/", url);
Ici, seuls les "some.jar' objet d'une recherche sur tout l'utilisation de ce ResourceFinder instance.
Il y a aussi une classe utilitaire appelé UrlSet
ce qui peut rendre la sélection des Url depuis le classpath très facile.
ClassLoader webAppClassLoader = Thread.currentThread().getContextClassLoader();
UrlSet urlSet = new UrlSet(webAppClassLoader);
urlSet = urlSet.exclude(webAppClassLoader.getParent());
urlSet = urlSet.matching(".*acme-.*.jar");
List<URL> urls = urlSet.getUrls();
Autre "service" styles
Disons que vous voulez appliquer l' ServiceLoader
type de concept à la refonte de gestion des URL et trouver/chargement de la java.net.URLStreamHandler
pour un protocole spécifique.
Voici comment vous pourriez disposition de les services dans votre classpath:
META-INF/java.net.URLStreamHandler/foo
META-INF/java.net.URLStreamHandler/bar
META-INF/java.net.URLStreamHandler/baz
Où foo
est un fichier texte qui contient le nom de la mise en œuvre des services tout comme avant. Maintenant, dire que quelqu'un crée un foo://...
d'URL. Nous pouvons trouver de la mise en œuvre pour que rapidement, par l'intermédiaire de:
ResourceFinder finder = new ResourceFinder("META-INF/");
Map<String, Class<? extends URLStreamHandler>> handlers = finder.mapAllImplementations(URLStreamHandler.class);
Class<? extends URLStreamHandler> fooHandler = handlers.get("foo");
Autre "service" styles 2
Supposons que vous vouliez mettre certaines informations de configuration dans votre fichier de service, de sorte qu'il contient plus que juste un nom de classe. Voici un autre style qui résout services à des fichiers de propriétés. Par convention, l'une des clés serait les noms de classe et les autres touches serait injectable propriétés.
Donc, ici, red
est un fichier de propriétés
META-INF/org.acme.Plugin/red
META-INF/org.acme.Plugin/blue
META-INF/org.acme.Plugin/green
Vous pouvez regarder les choses de la même façon qu'avant.
ResourceFinder finder = new ResourceFinder("META-INF/");
Map<String,Properties> plugins = finder.mapAllProperties(Plugin.class.getName());
Properties redDefinition = plugins.get("red");
Voici comment vous pouvez utiliser ces propriétés avec xbean-reflect
, une autre petite bibliothèque qui peut vous donner cadre-gratuit Cio. Vous donnez simplement le nom de la classe et quelques paires nom / valeur, et il permettra de construire et de les injecter.
ObjectRecipe recipe = new ObjectRecipe(redDefinition.remove("className").toString());
recipe.setAllProperties(redDefinition);
Plugin red = (Plugin) recipe.create();
red.start();
Voici à quoi cela peut ressembler "orthographié" en forme longue:
ObjectRecipe recipe = new ObjectRecipe("com.example.plugins.RedPlugin");
recipe.setProperty("myDateField","2011-08-29");
recipe.setProperty("myIntField","100");
recipe.setProperty("myBooleanField","true");
recipe.setProperty("myUrlField","http://www.stackoverflow.com");
Plugin red = (Plugin) recipe.create();
red.start();
L' xbean-reflect
bibliothèque est une étape au-delà de l'intégré dans JavaBeans API, mais un peu mieux, sans vous obliger à aller tout le chemin vers un plein sur Cio cadre comme Guice ou au Printemps. Il prend en charge l'usine de méthodes et constructeur args et setter/champ d'injection.
Pourquoi le ServiceLoader si limitée?
Obsolète code dans la JVM des dommages-intérêts le langage Java lui-même. Beaucoup de choses sont taillées pour l'os avant d'être ajoutés à la JVM, parce que vous ne pouvez pas ajuster par la suite. L' ServiceLoader
est un excellent exemple de cela. L'API est limitée et OpenJDK mise en œuvre est quelque part autour de 500 lignes, y compris javadoc.
Il n'y a rien de compliqué, il y et son remplacement est facile. Si cela ne fonctionne pas pour vous, ne l'utilisez pas.
Classpath portée
Api de côté, dans la pure pratique de limiter le champ de l'Url de recherche est la vraie solution à ce problème. App les Serveurs ont beaucoup d'Url par eux-mêmes, pas y compris les bocaux dans votre application. Tomcat 7 sur OSX, par exemple, a environ 40~ Url du StandardClassLoader seul (ce qui est le parent de tous les webapp chargeurs de classes).
Le plus grand de votre serveur d'application le plus à même une simple recherche prendra.
La mise en cache n'aide pas si vous l'intention de chercher plus d'une entrée. Ainsi, il peut ajouter un peu de mal fuites. Peut être un réel perdre-perdre du scénario.
Étroite de l'Url vers le 5 ou 12 qui compte vraiment pour vous et vous pouvez faire toutes sortes de services de chargement et de ne jamais l'avis de la frapper.