java SPI机制(转)_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > java SPI机制(转)

java SPI机制(转)

 2013/12/18 10:09:06  jackiee_cn  程序员俱乐部  我要评论(0)
  • 摘要:FromServiceLoaderjavadoc:Aserviceisawell-knownsetofinterfacesandclasses.Aserviceproviderisaspecificimplementationofaservice.Theclassesinaprovidertypicallyimplementtheinterfacesandsubclasstheclassesdefinedintheserviceitself.SinceJDK6,asimpleservice
  • 标签: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.

[java] view plaincopyprint?
  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.

?

[java] view plaincopyprint?
  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.

?

[java] view plaincopyprint?
  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.

?

[java] view plaincopyprint?
  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.

?

[plain] view plaincopyprint?
  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 Framework,?Services?are also supported through three differentFactoryBeans,?ServiceFactoryBean,?ServiceListFactoryBean,?ServiceLoaderFactoryBean. ? 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. ? Download Main Project:?https://github.com/downloads/maggandalf/Blog-Examples/reader.zip
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 ? Music:?http://www.youtube.com/watch?v=5cV_sIdpXeA&feature=related


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

发表评论
用户名: 匿名