ServiceLoader

本文介绍 Java SPI 机制的工作原理及其实现方式。通过示例展示了如何使用 SPI 机制加载和选择服务提供者,适用于多种语言翻译场景。此外,还讨论了 SPI 在 JDK 组件中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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"));

}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值