... h2. Introduction
When we use an approach +service based+ we have to deal with interfaces (i.e. _contracts_), factories and implementations. We need a mechanism to discovery the right implementation for specific service. A commons solution is to use a +*system property*+ that contain the *full qualified name (FQN)* of the implementation class and then the factory will use this to instantiate the implementation. *Service Provider Interface* allow us to use a standard technique to discovery a right implementation on a desired interface (i.e. _contract_ )
h2. SPI
This technique consist of put a file, whose name is contract's name , within jar under path *META-INF/services*. For example if the interface is _org.bsc.spi.MyContract_ into the jar the file will be:
{noformat} META-INF/services/org.bsc.spi.MyContract {noformat}
and within this file must be put just one line containing the FQN of the implementation.
At this point the steps to create a SPI application will be:
# create a contract (i.e. project containing interface) # create a provider that implements the contract and publish implementation through SPI service # create a factory (consumer) that discovery implementation and return the instance
h2. Java Example
h3. Create a contract
{code:java}
package org.bsc.spi;
public interface MyContract {
String test(); }
{code}
h3. Create a provider
As provider we mean an implementation of a contract. See example below:
{code} public class MyProvider implements MyContract {
public String test() { return "hello world!"; } } {code}
To publish this provider through the SPI we have found a very useful project [https://metainf-services.dev.java.net/] that allow us to write the SPI infos simply using the annotation *@MetaInfServices*. See example below
{code} @MetaInfServices public class MyProvider implements MyContract {
public String test() { return "hello world!"; } } {code}
to use this project from maven you have to put the following dependency in your pom
{code:xml|title=pom fragment} <repositories> <repository> <id>java.net</id> <url>http://download.java.net/maven/2/</url> </repository> </repositories> <dependencies>
<dependency> <groupId>org.kohsuke.metainf-services</groupId> <artifactId>metainf-services</artifactId> <version>1.1</version> <optional>true</optional> <type>jar</type> </dependency>
{code}
Note: *optional* means that this dependency will be not include in project that refer to your provider project. Run *clean* & *install* (or *package*) and automatically the SPI information will be included in your jar
h3. Create a Factory
Finally we can develop the factory that discover provider and instantiate it {code:java}
public class MyContractFactory {
public static MyContract createInstance() throws Exception { java.io.InputStream is = MyContractFactory.class.getClassLoader().getResourceAsStream("META-INF/services/org.bsc.spi.MyContract");
java.io.LineNumberReader r = new LineNumberReader( new java.io.InputStreamReader(is));
String fqn = r.readLine();
return (MyContract)Class.forName(fqn).newInstance(); }
} |