package ycl.learn.effective.java; public interface ServiceAPI { /** * translate context to appropriate language. * * @param content * @return */ public String translate(String content); }
package ycl.learn.effective.java; public class ChineseServiceSPI implements ServiceAPI{ public String translate(String content) { String trans = content.equals("Ch")?"Chinese":null; return trans; } }
package ycl.learn.effective.java; public class EnglishServiceSPI implements ServiceAPI{ public String translate(String content) { String trans = content.equals("En")?"Englis":null; return trans; } }
package ycl.learn.effective.java; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ServiceLoader; public class BaseServiceSPI{ private ServiceLoader<ServiceAPI> sl; 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; } BufferedReader rd; try { rd = new BufferedReader(new InputStreamReader(is, "UTF-8")); } catch (java.io.UnsupportedEncodingException e) { rd = new BufferedReader(new InputStreamReader(is)); } 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"); System.out.println(bs.translate("Ch")); System.out.println("==="); System.out.println(bs.translate("En")); // 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")); } }