Dubbo作为分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案。以微内核+插件机制作为底座,为上层提供易拓展特性,微内核和插件的核心即spi机制。介绍spi肯定离不开sdk提供的spi,因此分成上下两节分别介绍sdk spi和dubbo spi。本节主要有四部分,安排如下:
spi(Service provider interface,服务发现机制)通俗的讲即接口制定者制定规范, 实现接口的提供者按这种规范去指定接口的实现类(通过在META-INF/services下创建文件名为接口名的文件,在文件中指定当前接口的实现类),运行时调用方到指定地方读取配置得到当前接口的实现类,无需通过硬编码的方式在代码中指定接口的实现类,从而实现解耦。
package xu.jiang.hua.jdk.service;
public interface IService {
public void doSomeThing(String name);
}
package xu.jiang.hua.jdk.service.impl;
import xu.jiang.hua.jdk.service.IService;
public class WriteService implements IService {
@Override
public void doSomeThing(String name) {
System.out.println("写服务:"+name);
}
}
package xu.jiang.hua.jdk.service.impl;
import xu.jiang.hua.jdk.service.IService;
public class ReadService implements IService {
@Override
public void doSomeThing(String name) {
System.out.println("读服务:"+name);
}
}
package xu.jiang.hua.jdk;
import xu.jiang.hua.jdk.service.IService;
import java.util.Iterator;
import java.util.ServiceLoader;
public class TestMain {
public static void main(String[] args) {
ServiceLoader<IService> service = ServiceLoader.load(IService.class);
Iterator<IService> it = service.iterator();
while (it.hasNext()){
IService s = it.next();
s.doSomeThing("zzz");
}
}
}
因核心类为ServceiLoader,代码量不大,则整体贴出来。如下所示:
public final class ServiceLoader<S>
implements Iterable<S>
{
private static final String PREFIX = "META-INF/services/";
// The class or interface representing the service being loaded
private final Class<S> service;
// The class loader used to locate, load, and instantiate providers
private final ClassLoader loader;
// The access control context taken when the ServiceLoader is created
private final AccessControlContext acc;
// Cached providers, in instantiation order
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// The current lazy-lookup iterator
private java.util.ServiceLoader.LazyIterator lookupIterator;
public void reload() {
//
providers.clear();
//将初始化查找迭代器
lookupIterator = new LazyIterator(service, loader);
}
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
//加载
reload();
}
private static void fail(Class<?> service, String msg, Throwable cause)
throws ServiceConfigurationError
{
throw new ServiceConfigurationError(service.getName() + ": " + msg,
cause);
}
private static void fail(Class<?> service, String msg)
throws ServiceConfigurationError
{
throw new ServiceConfigurationError(service.getName() + ": " + msg);
}
private static void fail(Class<?> service, URL u, int line, String msg)
throws ServiceConfigurationError
{
fail(service, u + ":" + line + ": " + msg);
}
/**
* 解析配置中的文件
* @param service
* @param u
* @param r
* @param lc
* @param names
* @return
* @throws IOException
* @throws ServiceConfigurationError
*/
private int parseLine(Class<?> service, URL u, BufferedReader r, int lc,
List<String> names)
throws IOException, ServiceConfigurationError
{
String ln = r.readLine();
if (ln == null) {
return -1;
}
int ci = ln.indexOf('#');
if (ci >= 0) ln = ln.substring(0, ci);
ln = ln.trim();
int n = ln.length();
if (n != 0) {
if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
fail(service, u, lc, "Illegal configuration-file syntax");
int cp = ln.codePointAt(0);
if (!Character.isJavaIdentifierStart(cp))
fail(service, u, lc, "Illegal provider-class name: " + ln);
for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
cp = ln.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
fail(service, u, lc, "Illegal provider-class name: " + ln);
}
//如果不存在,则将读取的类加入集合
if (!providers.containsKey(ln) && !names.contains(ln))
names.add(ln);
}
return lc + 1;
}
private Iterator<String> parse(Class<?> service, URL u)
throws ServiceConfigurationError
{
InputStream in = null;
BufferedReader r = null;
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
//循环读取配置的类名,直到读完为止。
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
return names.iterator();
}
//赖迭代器,用于遍历文件中的所有内容
private class LazyIterator
implements Iterator<S>
{
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
Iterator<String> pending = null;
String nextName = null;
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
//META-SERVICE/services/+类名
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
//通过类加载器加载文件
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
//首次进入时pengding为null会进入循环,以及pengding不存在下一个时也进入循环
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
//调用parse方法,parse会将配置里面的类全部解析出来存放在ArrayList里面,但只返回list的迭代器
pending = parse(service, configs.nextElement());
}
//在判断是否存在下一个时,将取到的赋值给nextName,则在调用nextService时,直接用nextName
nextName = pending.next();
return true;
}
private S nextService() {
/**
* 执行nextService之前,其实已经通过hasNextService判断是否还要下一个service。
* 如果存在,才会调用nextService.此处再次判断,如果为false,则抛出异常
*/
if (!hasNextService())
throw new NoSuchElementException();
//
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
//加载类
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
//判断c是否是servcie的实现类
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
//实例化
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}
public boolean hasNext() {
if (acc == null) {
//
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() {
return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public S next() {
if (acc == null) {
//
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() {
return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* @return An iterator that lazily loads providers for this loader's
* service
*/
public Iterator<S> iterator() {
return new Iterator<S>() {
Iterator<Map.Entry<String,S>> knownProviders
= providers.entrySet().iterator();
public boolean hasNext() {
if (knownProviders.hasNext())
return true;
//调用懒迭代器的hasNext
return lookupIterator.hasNext();
}
public S next() {
if (knownProviders.hasNext())
return knownProviders.next().getValue();
//调用懒迭代器的next
return lookupIterator.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public static <S> java.util.ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new java.util.ServiceLoader<>(service, loader);
}
public static <S> java.util.ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return java.util.ServiceLoader.load(service, cl);
}
public static <S> java.util.ServiceLoader<S> loadInstalled(Class<S> service) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
ClassLoader prev = null;
while (cl != null) {
prev = cl;
cl = cl.getParent();
}
return java.util.ServiceLoader.load(service, prev);
}
/**
* Returns a string describing this service.
*
* @return A descriptive string
*/
public String toString() {
return "java.util.ServiceLoader[" + service.getName() + "]";
}
}
下面根据demo进行debug看看具体执行流程,整体流程分成三部分:
A、实例化ServiceLoader;
B、执行hasNext判断是否还存在service;
C、执行next得到service;
可以看到lookupIterator = new LazyIterator(service, loader),此时并没有加载文件(延迟加载);
因ServiceLoader实现Iterable接口,因此执行Iterator it = service.iterator()时返回ServiceLoader自身实现iterator,如下所示:
执行LazyIterator的hasNextService方法时,会执行pase方法加载文件内容并存储在list中,但只返回list的迭代器,如下所示:
调用nextService:
在nextService中再次执行hasNextService检查是否还存在service。如果不存在,则抛出异常。如果存在则通过类加载器加载类并进行实例化,然后存入provider中并返回服务类的实例。
调用迭代器的hasNext方法直到以下返回false跳出循环,完成所有接口实现类的实例化。
返回list(里面保存的ServiceName)迭代器的pending已经遍历完,同时configs也不再有下一个元素,则返回false,则结束整个遍历。
通过demo深入分析 jdk spi加载机制,为后续学习理解dubbo spi打下基础。想一下,jdk的spi有没有什么缺陷与不足?预知后事如何,且听下回分解!