Since Java 6, a mechanism was introduced for Java code to automatically discover “plugin” service implementations for JARs within the classpath.
As well as being used by Java to find XML parsing & Image IO implementations, it’s an interesting mechanism & can be used to make user applications extensible too.
Define a Service
First, we’ll cover some quick terminology.
- a ‘Service’ is the API you want plugins to be able to implement — probably an interface.
- ‘Service Provider’ is the plugin, which you want to be able to discover & load automatically.
- ‘Service Provider Interface’ (SPI) is the Service API with any associated classes; data-transfer objects, exceptions etc.
To define the Service, you should declare an interface or abstract class — for example, SearchService
.
public interface SearchService { public List<Result> search (String text); }
We can see here that a ‘Result’ class might be part of the SPI, and can be defined as part of the base Search service.
Create a Plugin
Since service implementations are found by JAR loading, you’ll probably need to define & build a JAR. This could be a separate or sub-project in your workspace.
Let’s suppose we are building a FuzzyTextSearch
implementation of the SearchService.
Four simple steps are required:
- you build a
META-INF/services
subdirectory into your JAR file. - in the ‘services’ directory, create a file for each service you are implementing. (in your case, this would be file
META-INFA/services/some.package.SeachService
). - in that file, declare the name of your implementation class(es).
- you can then use the
java.util.ServiceLoader
API to discover & load registered services.
An example of the service provider file:
# providers of SearchService # (comment lines begin with pound) some.package.FuzzyTextSearch
Your application can then find and load service implementations like this:
ServiceLoader<SearchService> loader = ServiceLoader.load(SearchService.class); for (SearchService plugin : loader) { // search via plugin... }
The ServiceLoader mechanism enables your code to discover & use plugin implementations, without the base service being aware of or coded for the specific plugins you ship.
Conclusion
Service discovery & loading is an important mechanism which can be used for extensible applications, as well as being the means of extensibility within Java libraries.
Compared with other extensibility mechanisms (configuration-specific XML bean files, or Spring/ CDI component-scanning), ServiceLoader discovery provides a basic & effective plugin capability.
Pros: simple, easy to ease, fast loading. Con: similar to component-scanning, there is no ability to parameterize plugins.
Lastly, we’ll note that ServiceLoader is a Java standard; but that there are a few extra requirements to using it in OSGi environments. See the references if this is relevant to you.
See also: