From ServiceLoader javadoc: A service is a well-known set of interfaces and classes. A service provider is a specific implementation of a service. The classes in a provider typically implement the interfaces and subclass the classes defined in the service itself.
Since JDK 6, a simple service-provider loading facility is implemented. As is suggested it is simple, you cannot understand that implementation as a complex system for implementing plugins for your application, but can help you in many situations where you want implementations of a service could be discovered by other module automatically.
What I really like about using JDK Services is that your services do not have any dependency to any class. Moreover for registering a new implementation of a service, you just have to put jar file into classpath and nothing more.
Now I will explain the service we are going to implement, and then I will show you how to code it.
We want to implement a system that depending on kind of structured input (comma-separated value, tab-separated value, ...) returns a String[] of each value; so for example you can receive input a,b,c,dor 1<tab>2<tab>3<tab>4 and the system should return an array with [a, b, c, d] or [1, 2, 3, 4].
So our system will have three Java projects.
One defining service contract (an interface) and, because of teaching purpose, a main class where internet media type, for example text/csv, is received with input data. Then using a factory class that I have created, it will ask which registered service can transform input to String[].
And two projects each one implementing a service following defined contract, one for comma-separated values and another one for tab-separated values.
Let's see the code:
Main project (reader) is composed by an interface, a main class and a factory class.
The most important part is Decode interface which defines service contract.
- public interface Decode {
- boolean isEncodingNameSupported(String encodingName);
- String[] getContent(String data);
- }
public interface Decode { boolean isEncodingNameSupported(String encodingName); String[] getContent(String data); }
Two operations are defined, one that returns if service supports given input, and another that transforms data to String[].
DecodeFactory class is responsible for finding an implementation service that supports required encoding. In fact, this class encapsulates java.util.ServiceLoader calls. ServiceLoader class is in charge of load registered services.
- public class DecodeFactory {
- private static ServiceLoader decodeSetLoader = ServiceLoader.load(Decode.class);
- public static Decode getDecoder(String encodingName) throws UnsupportedEncodingException {
- for (Decode decode : decodeSetLoader) {
- if(decode.isEncodingNameSupported(encodingName)) {
- return decode;
- }
- }
- throw new UnsupportedEncodingException();
- }
- }
public class DecodeFactory { private static ServiceLoader decodeSetLoader = ServiceLoader.load(Decode.class); public static Decode getDecoder(String encodingName) throws UnsupportedEncodingException { for (Decode decode : decodeSetLoader) { if(decode.isEncodingNameSupported(encodingName)) { return decode; } } throw new UnsupportedEncodingException(); } }
At line 3 we are loading all services that are registered in classpath. At line 7 we only iterate through all services asking if given encoding name is supported.
And finally main class.
- public class App {
- public static void main(String[] args) throws UnsupportedEncodingException {
- String encodeName = args[0];
- String data = args[1];
- Decode decoder = DecodeFactory.getDecoder(encodeName);
- System.out.println(Arrays.toString(decoder.getContent(data)));
- }
- }
public class App { public static void main(String[] args) throws UnsupportedEncodingException { String encodeName = args[0]; String data = args[1]; Decode decoder = DecodeFactory.getDecoder(encodeName); System.out.println(Arrays.toString(decoder.getContent(data))); } }
And now if you run this class with java -jar reader.jar "text/cvs" "a, b, c, d", anUnsupportedEncodingException will be thrown. Now we are going to implement our first service. Note that reader project will not be modified nor recompiled.
First service we are going to implement is one that can support comma-separated values encoding. Only one class and one file are important.
CSV class is an implementation of Decode interface and transforms comma-separated values.
- public class CSVDecoder implements Decode {
- private static final String DELIMITER = Character.toString(DecimalFormatSymbols.getInstance().getPatternSeparator());
- public boolean isEncodingNameSupported(String encodingName) {
- return "text/csv".equalsIgnoreCase(encodingName.trim());
- }
- public String[] getContent(String data) {
- List values = new LinkedList();
- StringTokenizer parser = new StringTokenizer(data, DELIMITER);
- while(parser.hasMoreTokens()) {
- values.add(parser.nextToken());
- }
- return values.toArray(new String[values.size()]);
- }
- }
public class CSVDecoder implements Decode { private static final String DELIMITER = Character.toString(DecimalFormatSymbols.getInstance().getPatternSeparator()); public boolean isEncodingNameSupported(String encodingName) { return "text/csv".equalsIgnoreCase(encodingName.trim()); } public String[] getContent(String data) { List values = new LinkedList(); StringTokenizer parser = new StringTokenizer(data, DELIMITER); while(parser.hasMoreTokens()) { values.add(parser.nextToken()); } return values.toArray(new String[values.size()]); } }
As you can see a simple StringTokenizer class. Only take care that this class is Locale sensitive, countries where comma (,) is used as decimal delimiter, separation character is semicolon (;).
And next important file is a file that is placed into META-INF. This file contains a pointer to service implementation class.
This file should be in META-INF/services and should be called as interface full qualified name. In this case org.alexsotob.reader.Decode. And its content should be service implementation full qualified name.
- org.alexsotob.reader.csv.CSVDecoder #Comma-separated value decode.
org.alexsotob.reader.csv.CSVDecoder #Comma-separated value decode.
See that reader project has not been modified and its behaviour has been changed. Now you can develop new implementations for decoding new inputs, and you should only take care of copying them into classpath.
Only take care that all services should have a default constructor with no arguments.
And for those who use Spring Framework, Services are also supported through three differentFactoryBeans, ServiceFactoryBean, ServiceListFactoryBean, ServiceLoaderFactoryBean.
Download Comma-Separated Values Service Project: https://github.com/downloads/maggandalf/Blog-Examples/csvDec.zip
Download Tab-Separated Values Service Project: https://github.com/downloads/maggandalf/Blog-Examples/tsvDec.zip
原文链接:http://alexsotob.blogspot.com/2011/11/en-ti-puedo-ver-la-libertad-tu-me-haces.html
相关推荐
Java类加载及SPI机制.pdf
Java SPI机制详解.md
Java SPI 机制(SPI实战+ServiceLoader源码分析+SPI 应用场景+破坏双亲委派)
这个资源是我的博客《java SPI机制学习笔记》中的提供服务的jar包
主要介绍了Java SPI机制原理及代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
Java 基础(8-8)-Java常用机制 - SPI机制详解
spi服务提供者和服务调用Demo,包括服务接口定义,服务实现,服务配置以及服务调用示例。
Java类加载及SPI机制
在本篇文章里小编给大家整理的是一篇关于Java SPI 机制知识点总结内容,需要的朋友们可以参考下。
主要介绍了Java的SPI机制实例详解的相关资料,需要的朋友可以参考下
主要介绍了深入学习Java中的SPI机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
一款基于hdfs的ftp服务器,通过java的spi机制内置支持多hadoop版本,自带依赖,无需编译即可使用。
JAVA SPI机制的文档总结,包含相关演示工程
SPI(Service Provider Interface)是 Java 中一种基于接口的服务发现机制,旨在实现代码解耦和可扩展性。通过 SPI,开发者可以定义一组接口,而具体的实现则由不同的提供者来提供,实现了解耦的目的。 SPI 机制使得...
1. 在META-INF/services/目录中创建以接口完全限定名命名的文件,该文件内容为Api具体实现类的全限定名 2. 使用ServiceLoader类
主要介绍了详解java实践SPI机制及浅析源码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
可以使用Java 提供的SPI机制 什么是SPI?SPI和API的区别 SPI SPI的全称是Service Provider Interface,是Java提供的可用于第三方实现和扩展的机制,通过该机制,我们可以实现解耦,SPI接口方负责定义和提供默认...
Java SPI 机制详解 Java 语法糖详解 集合 知识点/面试题总结 : Java 集合常见知识点&面试题总结(上) (必看 ) Java 集合常见知识点&面试题总结(下) (必看 ) Java 容器使用注意事项总结 源码分析 : ArrayList 源码...
Java SPI 机制详解 Java 语法糖详解 集合 知识点/面试题总结: Java 集合常见知识点&面试题总结(上) (必看 ) Java 集合常见知识点&面试题总结(下) (必看 ) Java 容器使用注意事项总结 源码分析: ArrayList 源码+...
人工智能-hadoop