`

java SPI机制(转)

    博客分类:
  • java
阅读更多

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.

  1. public interface Decode {  
  2.   
  3.  boolean isEncodingNameSupported(String encodingName);  
  4.  String[] getContent(String data);  
  5.    
  6. }  
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.

 

  1. public class DecodeFactory {  
  2.   
  3.  private static ServiceLoader decodeSetLoader = ServiceLoader.load(Decode.class);  
  4.    
  5.  public static Decode getDecoder(String encodingName) throws UnsupportedEncodingException {  
  6.     
  7.   for (Decode decode : decodeSetLoader) {  
  8.    if(decode.isEncodingNameSupported(encodingName)) {  
  9.     return decode;  
  10.    }  
  11.   }  
  12.   
  13.   throw new UnsupportedEncodingException();  
  14.  }  
  15. }  
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.

 

  1. public class App {  
  2.    
  3.  public static void main(String[] args) throws UnsupportedEncodingException {  
  4.     
  5.   String encodeName = args[0];  
  6.   String data = args[1];  
  7.     
  8.   Decode decoder = DecodeFactory.getDecoder(encodeName);  
  9.   System.out.println(Arrays.toString(decoder.getContent(data)));  
  10.     
  11.  }  
  12. }  
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.

 

  1. public class CSVDecoder implements Decode {  
  2.   
  3.  private static final String DELIMITER = Character.toString(DecimalFormatSymbols.getInstance().getPatternSeparator());  
  4.    
  5.  public boolean isEncodingNameSupported(String encodingName) {  
  6.   return "text/csv".equalsIgnoreCase(encodingName.trim());  
  7.  }  
  8.   
  9.  public String[] getContent(String data) {  
  10.     
  11.   List values = new LinkedList();  
  12.     
  13.   StringTokenizer parser = new StringTokenizer(data, DELIMITER);  
  14.     
  15.   while(parser.hasMoreTokens()) {  
  16.    values.add(parser.nextToken());  
  17.   }  
  18.     
  19.   return values.toArray(new String[values.size()]);  
  20.     
  21.  }  
  22.   
  23. }  
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.

 

  1. org.alexsotob.reader.csv.CSVDecoder #Comma-separated value decode.  
org.alexsotob.reader.csv.CSVDecoder #Comma-separated value decode.

 

And now you can package this project, and you can reexecute reader project but with generated jar(csv.jar) into classpath. And now output will be an array with a, b, c and d characters instead of unsupported exception.

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 FrameworkServices are also supported through three differentFactoryBeansServiceFactoryBeanServiceListFactoryBeanServiceLoaderFactoryBean.
 
As I have noted at start of this post, JDK services is a simple (yet powerful) solution if you need to create a simple plugins system. In my case it has been enough with JDK services, and I have never required more complex structure; but in case you are thinking about a complete plugin solution you can use JPF that offers a solution like Eclipse plugins, or even OSGI.
 
I wish you have found this post useful and now you know (if you didn't know yet), an easy solution to develop modules that are plugged and play.
 
 


原文链接:http://alexsotob.blogspot.com/2011/11/en-ti-puedo-ver-la-libertad-tu-me-haces.html

分享到:
评论

相关推荐

    Java SPI 机制(SPI实战+ServiceLoader源码分析+SPI 应用场景+破坏双亲委派)

    Java SPI 机制详解 Java SPI 机制,全称 Service Provider Interface,是 Java 内置的服务发现机制。SPI 机制的主要思想是解耦,通过提供一个标准接口,允许第三方提供实现类,而无需在程序中硬编码。SPI 机制广泛...

    Java类加载及SPI机制.pdf

    而SPI机制则提供了一种通用的方式来扩展Java平台功能,使得Java开发者可以在不修改源码的情况下增强平台或第三方库的功能。在实际开发过程中,深入理解和正确使用这两个机制,对于提升软件的灵活性、可维护性以及可...

    Java SPI机制详解.md

    Java SPI机制详解.md

    java SPI机制实现服务接口和服务实现分离源码Demo

    SPI机制的核心是`java.util.ServiceLoader`类,它允许我们按照约定在`META-INF/services`目录下创建配置文件,来指定哪些类实现了特定的服务接口。 服务接口定义:在SPI机制中,首先我们需要定义一个服务接口,这个...

    Java 基础(8-8)-Java常用机制 - SPI机制详解.pdf

    Java SPI 机制详解 Java SPI(Service Provider Interface)机制是一种服务提供发现机制,能够实现框架的扩展和替换组件,主要被框架的开发人员使用。SPI 机制的核心思想是将装配的控制权移到程序之外,在模块化...

    java spi实现工程

    Java SPI(Service Provider Interface)是Java平台提供的一种服务发现机制,允许JVM在运行时动态加载服务提供商。这个机制使得开发者可以扩展应用的功能,而无需修改原有代码。SPI的核心概念在于,服务接口由主程序...

    java SPI的jar包

    这个资源是我的博客《java SPI机制学习笔记》中的提供服务的jar包

    java spi 可运行 demo

    Java SPI(Service Provider Interface)是Java标准库提供的一种服务发现机制,它允许程序在运行时动态地查找并...`spitest`项目可能是一个用于演示如何使用和配置SPI的示例,可以帮助开发者更好地理解和应用SPI机制。

    Java SPI机制原理及代码实例

    总结,Java SPI机制为开发者提供了一种灵活的服务发现和加载方式,使得软件系统能够更好地适应变化,降低耦合度,增强系统的可扩展性和可维护性。在实际项目中,合理利用SPI可以提高软件组件的可插拔性,方便集成和...

    Java SPI Demo

    Java SPI(Service Provider Interface)是Java平台提供的一种服务发现机制,允许JVM在运行时动态加载和使用服务提供商。这个机制使得开发者可以扩展应用程序的功能,而不需要修改应用程序的代码。在Java SPI中,...

    Java类加载及SPI机制.zip

    而SPI机制是Java平台提供的一种灵活的服务发现和加载方式,它促进了模块化开发和插件化的实现,增强了软件的可扩展性。这两个知识点对于Java开发者来说是必不可少的,深入掌握能提高开发效率和代码质量。

    Java SPI的简单小实例

    Java SPI机制的应用非常广泛,例如JDBC中的数据库连接驱动接口、Java中的日志记录接口等。这些接口都可以通过SPI机制来加载和实例化服务提供者,从而提供一个灵活的服务加载机制。 在实际应用中,Java SPI机制可以...

    Java的SPI机制实例详解

    Java SPI机制实例详解 Java的SPI机制实例详解是Java提供的一种服务提供商接口机制,英文全名为Service Provider Interface。SPI机制实例详解主要是面向厂商或者插件的,普通开发人员可能不熟悉。Java的SPI机制实例...

    java对于MP3的spi

    总结起来,Java中的MP3 SPI是通过Java Sound API的SPI机制来扩展其对MP3音频格式的支持。`MpegAudioSPI1.9.5`是一个实现了MP3 SPI的库,它提供了解码和播放MP3文件的功能。通过使用这个库,开发者可以轻松地在Java...

    java spi-demo示例

    SPI机制使得开发者可以扩展程序的功能,而无需修改原代码,极大地提高了软件的可扩展性和模块化。 在Java SPI中,服务接口通常定义在`META-INF/services`目录下的一个特定配置文件中,文件名为`接口全限定名`。例如...

    java spi 学习记录

    SPI机制使得开发者可以扩展程序的功能,而无需修改原代码,极大地提高了软件的可扩展性和模块化。 标题“Java SPI 学习记录”表明我们将探讨这个重要的Java特性,并分享学习过程中的一些理解和实践。这篇博客可能...

    java的spi测试

    Java的Service Provider Interface (SPI) 是一种用于在运行时动态发现和加载服务提供者实现的机制。这个功能在Java标准库中被广泛使用,允许开发者扩展JVM的功能,而无需修改核心API。在这个"java的spi测试"中,我们...

    深入学习Java中的SPI机制

    "深入学习Java中的SPI机制" Java中的SPI(Service Provider Interface)机制是一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用。SPI机制的主要思想是将装配的控制权移到程序...

    java-spi学习例子

    Java SPI(Service Provider Interface)是Java平台提供的一种服务发现机制,允许JVM在运行时动态加载服务实现。这个机制使得开发者可以扩展已存在的应用程序或库,而无需修改原代码。SPI的核心理念是将服务接口与其...

    一款基于hdfs的ftp服务器,通过java的spi机制内置支持多hadoop版本,自带依赖,无需编译即可使用。.zip

    “通过java的spi机制内置支持多hadoop版本”这部分涉及到Java的Service Provider Interface (SPI)机制。SPI是Java提供的一种服务发现框架,允许第三方开发者扩展JVM内置的服务。在这个FTP服务器中,SPI被用来识别和...

Global site tag (gtag.js) - Google Analytics