<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Java Archive - dahlen.org</title>
	<atom:link href="https://www.dahlen.org/tag/java/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dahlen.org/tag/java/</link>
	<description>Private Webseite der Familie Dahlen</description>
	<lastBuildDate>Tue, 07 Sep 2021 09:12:36 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
	<item>
		<title>Abhängigkeiten vermeiden mit der Java Service Provider Spezifikation</title>
		<link>https://www.dahlen.org/2009/08/14/abhangigkeiten-vermeiden-mit-der-java-service-provider-spezifikation/</link>
					<comments>https://www.dahlen.org/2009/08/14/abhangigkeiten-vermeiden-mit-der-java-service-provider-spezifikation/#comments</comments>
		
		<dc:creator><![CDATA[christoph]]></dc:creator>
		<pubDate>Fri, 14 Aug 2009 11:52:03 +0000</pubDate>
				<category><![CDATA[Informatik]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Software]]></category>
		<guid isPermaLink="false">https://www.dahlen.org/?p=197</guid>

					<description><![CDATA[<p>Durch ein aktuelles Projekt der Cassini Consulting konnte ich mich endlich Mal tiefer mit einem grundsätzlichen Problem beschäftigen: der losen Kopplung eines Service Provider Interfaces (SPI) oder grundsätzlich eines API und seiner Implementierungen in Form von Plugins. Das Erstaunliche daran (überlagert von Themen wie Dependency Injection und Spring): Es gibt bereits seit dem JDK 1.3 [&#8230;]</p>
<p>Der Beitrag <a href="https://www.dahlen.org/2009/08/14/abhangigkeiten-vermeiden-mit-der-java-service-provider-spezifikation/">Abhängigkeiten vermeiden mit der Java Service Provider Spezifikation</a> erschien zuerst auf <a href="https://www.dahlen.org">dahlen.org</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Durch ein aktuelles Projekt der <a href="http://www.cassini.de/">Cassini Consulting</a> konnte ich mich endlich Mal tiefer mit einem grundsätzlichen Problem beschäftigen: der losen Kopplung eines Service Provider Interfaces (SPI) oder grundsätzlich eines <abbr title="Application Programmers Interface">API</abbr> und seiner Implementierungen in Form von <q>Plugins</q>. Das Erstaunliche daran (überlagert von Themen wie <a href="http://de.wikipedia.org/wiki/Dependency_Injection">Dependency Injection</a> und <a href="http://www.springsource.com/">Spring</a>): Es gibt bereits seit dem <abbr title="Java Development Kit">JDK</abbr> 1.3 eine sehr einfache Lösung für dieses Problem und sie funktioniert auch <q>ab Werk</q>: Die <em>Java Service Provider Spezifikation</em>.<br />
<span id="more-197"></span><br />
Die Problemstellung lässt sich am besten in Form eines Beispiels erläutern. Die Projektleitung kommt auf die irsinnige Idee, ein <q>Quoter</q>-Interface als Teil einer API zu definieren, über welches Texte in Anführungszeichen gefasst werden können. Etwa so:</p>
<pre><code>package demo.api;

public interface IQuoter {
        String quote(String s);
}
</code></pre>
<p>Dazu steuern die Entwickler eine Implementierung für den deutschen Sprachraum bei, welcher tief- und hochgestellte Anführungszeichen verwendet (über Unicode):</p>
<pre><code>package demo.impl;

public class GermanQuoter implements IQuoter {
        public String quote(String s) {
                return '\u201E' + s + '\u201D';
        }
}
</code></pre>
<p>Die Frage ist nun, wie kann man diese Implementierung verwenden,</p>
<ul>
<li>ohne sie direkt in der Applikation zu importieren und</li>
<li>ohne sie direkt in der API zu referenzieren?</li>
</ul>
<p>Die Lösung ist interessanterweise bereits seit dem JDK 1.3 Teil der Java Standard Edition (SE), auch wenn sie zwischen den Versionen einige Male den Platz gewechselt hat. Mit Java&#160;6 scheint sie nun aber ihren endgültigen Platz gefunden zu haben als  java.util.ServiceLoader.</p>
<p>Dafür muß die Implementierung als eigenes <abbr title="Java ARchive">JAR</abbr> ausgeliefert werden, denn folgende Punkte sind Voraussetzung für die Nutzung des ServiceLoader:</p>
<ol>
<li>im JAR muß ein Verzeichnis <em>META-INF/services</em> vorhanden sein</li>
<li>in diesem Verzeichnis muß für jede API Klasse (Interface oder abstrakte Klasse) eine Datei gleichen Namens liegen</li>
<li>in jeder dieser Dateien muß der vollqualifizierte Namen der Implementations-Klasse vermerkt sein</li>
</ol>
<p>Für unser Beispiel bedeutet das also, daß der Inhalt des Implementierungs-JAR wie folgt aussieht:<br />
<code>demo.impl.GermanQuoter<br />
META-INF/services/demo.api.IQuoter</code></p>
<p>Und die einzige Zeile in der Datei demo.api.IQuoter liest sich wie folgt:<br />
<code>demo.impl.GermanQuoter</code></p>
<p>Wie kommt nun unsere Applikation, welche die API und das Implementierungs-JAR im Klassenpfad hat an die Implementierung? Nun, über Aufruf der entsprechenden <q>Service Locator</q>, welche sich je nach JDK an verschiedenen Stellen finden:</p>
<ul>
<li>im JDK 1.3 als sun.misc.Service</li>
<li>im JDK 1.4 &#8211; 1.5 als javax.imageio.spi.ServiceRegistry(!)</li>
<li>seit dem JDK 1.6 endlich an richtiger Stelle als <a href="http://java.sun.com/javase/6/docs/api/java/util/ServiceLoader.html#load%28java.lang.Class%29">java.util.ServiceLoader</a></li>
</ul>
<p>Also schreiben wir uns für das JDK 1.6 eine kleine, generische Utility-Klasse, welche eine oder alle Implementierungen für eine API-Artefakt liefern kann:</p>
<pre><code>package demo.util;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;

public final class GenericServiceLocator {

    private GenericServiceLocator() {
    }

    public static &lt;T> T locate(final Class&lt;T> clazz) {
        final List services = locateAll(clazz);
        return services.isEmpty() ? (T) null : services.get(0);
    }

    public static &lt;T> List&lt;T> locateAll(final Class&lt;T> clazz) {

        final Iterator&lt;T> iterator = ServiceLoader.load(clazz).iterator();
        final List&lt;T> services = new ArrayList&lt;T>();

        while(iterator.hasNext()) {
            try {
                services.add(iterator.next());
            } catch (Error e) {
                e.printStackTrace(System.err);
            }
        }

        return services;

    }
}
</code></pre>
<p>Und so sieht dann der Aufruf aus unserer Beispiel-Applikation aus:</p>
<pre><code>package demo;

import demo.api.IQuoter;
import demo.util.GenericServiceLocator;

public class App {
    public static void main( String[] args ) {
        System.out.println(
                GenericServiceLocator
                    .locate(IQuoter.class)
                    .quote("Quote Me")
        );
    }
}
</code></pre>
<p>Das ist alles. Befinden sich API, Implementierung und Applikation im Klassenpfad, kann der Test über den Aufruf von<br />
<code>java -cp demo-api.jar;demo-impl.jar;demo-app.jar demo.App</code><br />
erfolgen. Das Ergebnis sollte auf Unicode-Terminals ein <tt>&#8222;Quote Me&#8221;</tt> sein. Auf der Windows&nbsp;XP Console ist es übrigens <tt>äQuote Meö</tt> &#8230;</p>
<p>Zum Ausprobieren ist das <a href="https://www.dahlen.org/wp-content/uploads/2009/08/spi-demo.zip">Beispiel-Projekt</a> als <a href="http://maven.apache.org/">Maven2</a> Multi-Module beigefügt, die Verwendung in eigenen Projekten ist &#8211; selbstverständlich &#8211; gestattet.</p>
<p>Der Beitrag <a href="https://www.dahlen.org/2009/08/14/abhangigkeiten-vermeiden-mit-der-java-service-provider-spezifikation/">Abhängigkeiten vermeiden mit der Java Service Provider Spezifikation</a> erschien zuerst auf <a href="https://www.dahlen.org">dahlen.org</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dahlen.org/2009/08/14/abhangigkeiten-vermeiden-mit-der-java-service-provider-spezifikation/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
