public BaseServiceSPI(ServiceLoader<ServiceAPI> sl){ this.sl = sl; }
/** * just find first one implement. * * @param clazz * @return * @throws Exception */ public static Object findServiceProvider(Class clazz) throws Exception { String factoryId =clazz.getName(); String serviceId = "META-INF/services/" + factoryId; InputStream is = null;
// First try the Context ClassLoader ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl != null) { is = cl.getResourceAsStream(serviceId); } else { // No Context ClassLoader, try the current ClassLoader cl = BaseServiceSPI.class.getClassLoader(); is = cl.getResourceAsStream(serviceId); }
if (is == null) { // No provider found return null; }
String factoryClassName = null; try { // XXX Does not handle all possible input as specified by the // Jar Service Provider specification factoryClassName = rd.readLine(); rd.close(); } catch (IOException x) { // No provider found return null; }
if (factoryClassName != null && !"".equals(factoryClassName)) {
// Note: here we do not want to fall back to the current // ClassLoader because we want to avoid the case where the // resource file was found using one ClassLoader and the // provider class was instantiated using a different one. Object p = clazz.cast(Class.forName(factoryClassName, true, cl) .newInstance()); return p; }
// No provider found return null; }
/** * auto match method * 1. if that is multiple implements, and you just want to auto choose. * * rule * *. sub class method is return null, if this params is not match of this process. * *. sub class method will be return not null, if this params is match of this process. * * @param content * @return */ public String translate(String content) { for(ServiceAPI serviceAPI: sl){ String trans = serviceAPI.translate(content); if(trans != null){ return trans; } } return null; }
}
package ycl.learn.effective.java;
import java.util.ServiceLoader;
public class TestServiceLoader {
public static void main(String args[]) throws Exception{ ServiceLoader<ServiceAPI> sl = ServiceLoader.load(ServiceAPI.class); BaseServiceSPI bs = new BaseServiceSPI(sl); //auto choose implements. //in this situation , you can add more and more implements. //as like add Languages.
//also the JDK components use Service Loader to implements. //you can read javax.xml.parsers.FactoryFinder // javax.xml.parsers.SAXParserFactory // javax.xml.parsers.DocumentBuilderFactory // 1. System property // 2. jdk-dir/lib/jaxp.properties set default implements //javax.xml.parsers.DocumentBuilderFactory javax.xml.parsers.SAXParserFactory // 3. META-INF/services/javax.xml.parsers.DocumentBuilderFactory ==> org.apache.crimson.jaxp.DocumentBuilderFactoryImpl // 4. if haven't find,user Crimson。ClassNotFound // summary, so you know what's xml parser you shoud be to use. // "javax.xml.parsers.SAXParserFactory", "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" // "javax.xml.parsers.DocumentBuilderFactory" "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl" // you can have multiple implements at one time, but you just choose one implement is ok. System.setProperty("javax.xml.parsers.SAXParserFactory", "org.apache.xerces.jaxp.SAXParserFactoryImpl");
// that's right, you can just get one implements as below. // you also can get all impements use rules to choose right one. ServiceAPI serviceAPI = (ServiceAPI) BaseServiceSPI.findServiceProvider(ServiceAPI.class); System.out.println(serviceAPI.translate("Ch")); System.out.println("hahaah"); System.out.println(serviceAPI.translate("En"));