此文已由作者赵计刚薪授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
dubbo-spi是在jdk-spi的基础上进行重写优化,下面看一下jdk-spi。
一、作用
二、实现方式
三、使用方法
注意:示例以Log体系为例,但是实际中的Log体系并不是这样来实现的。
1、pom.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 <groupId>com.hulk</groupId> 7 <artifactId>java-spi</artifactId> 8 <version>1.0-SNAPSHOT</version> 9 </project>
2、标准接口:com.hulk.javaspi.Log
1 package com.hulk.javaspi; 2 3 public interface Log { 4 void execute(); 5 }
3、具体实现1:com.hulk.javaspi.Log4j
1 package com.hulk.javaspi; 2 3 public class Log4j implements Log { 4 @Override 5 public void execute() { 6 System.out.println("log4j ..."); 7 } 8 }
4、具体实现2:com.hulk.javaspi.Logback
1 package com.hulk.javaspi; 2 3 public class Logback implements Log { 4 @Override 5 public void execute() { 6 System.out.println("logback ..."); 7 } 8 }
5、指定使用的实现文件:META-INF/services/com.hulk.javaspi.Log
1 com.hulk.javaspi.Logback
注意
1 com.hulk.javaspi.Logback 2 com.hulk.javaspi.Log4j
6、加载实现主类:com.hulk.javaspi.Main
1 package com.hulk.javaspi; 2 3 import java.util.Iterator; 4 import java.util.ServiceLoader; 5 6 public class Main { 7 public static void main(String[] args) { 8 ServiceLoader<Log> serviceLoader = ServiceLoader.load(Log.class); 9 Iterator<Log> iterator = serviceLoader.iterator(); 10 while (iterator.hasNext()) { 11 Log log = iterator.next(); 12 log.execute(); 13 } 14 } 15 }
注意:
现在来解析Main的源码。
四、源码解析
1、获取ServiceLoader
1 ServiceLoader<Log> serviceLoader = ServiceLoader.load(Log.class);
源码:
首先来看一下ServiceLoader的6个属性
1 private static final String PREFIX = "META-INF/services/";//定义实现类的接口文件所在的目录 2 private final Class<S> service;//接口 3 private final ClassLoader loader;//定位、加载、实例化实现类 4 private final AccessControlContext acc;//权限控制上下文 5 private LinkedHashMap<String,S> providers = new LinkedHashMap<>();//以初始化的顺序缓存<接口全名称, 实现类实例> 6 private LazyIterator lookupIterator;//真正进行迭代的迭代器
其中LazyIterator是ServiceLoader的一个内部类,在迭代部分会说。
1 public static <S> ServiceLoader<S> load(Class<S> service) { 2 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 3 return ServiceLoader.load(service, cl); 4 } 5 6 public static <S> ServiceLoader<S> load(Class<S> service, 7 ClassLoader loader) { 8 return new ServiceLoader<>(service, loader); 9 } 10 11 private ServiceLoader(Class<S> svc, ClassLoader cl) { 12 service = Objects.requireNonNull(svc, "Service interface cannot be null"); 13 loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl; 14 acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null; 15 reload(); 16 } 17 18 public void reload() { 19 providers.clear();//清空缓存 20 lookupIterator = new LazyIterator(service, loader); 21 }
这样一个ServiceLoader实例就创建成功了。在创建的过程中,我们看到还实例化了一个LazyIterator,该类下边会说。
2、获取迭代器并迭代
1 Iterator<Log> iterator = serviceLoader.iterator(); 2 while (iterator.hasNext()) { 3 Log log = iterator.next(); 4 log.execute(); 5 }
外层迭代器:
1 public Iterator<S> iterator() { 2 return new Iterator<S>() { 3 4 Iterator<Map.Entry<String,S>> knownProviders 5 = providers.entrySet().iterator(); 6 7 public boolean hasNext() { 8 if (knownProviders.hasNext()) 9 return true; 10 return lookupIterator.hasNext(); 11 } 12 13 public S next() { 14 if (knownProviders.hasNext()) 15 return knownProviders.next().getValue(); 16 return lookupIterator.next(); 17 } 18 19 public void remove() { 20 throw new UnsupportedOperationException(); 21 } 22 23 }; 24 }
从查找过程hasNext()和迭代过程next()来看。
下面来看一下,LazyIterator这个类。首先看一下他的属性:
1 Class<S> service;//接口 2 ClassLoader loader;//类加载器 3 Enumeration<URL> configs = null;//存放配置文件 4 Iterator<String> pending = null;//存放配置文件中的内容,并存储为ArrayList,即存储多个实现类名称 5 String nextName = null;//当前处理的实现类名称
其中,service和loader在上述实例化ServiceLoader的时候就已经实例化好了。
下面看一下hasNext():
1 public boolean hasNext() { 2 if (acc == null) { 3 return hasNextService(); 4 } else { 5 PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() { 6 public Boolean run() { return hasNextService(); } 7 }; 8 return AccessController.doPrivileged(action, acc); 9 } 10 } 11 12 private boolean hasNextService() { 13 if (nextName != null) { 14 return true; 15 } 16 if (configs == null) { 17 try { 18 String fullName = PREFIX + service.getName(); 19 if (loader == null) 20 configs = ClassLoader.getSystemResources(fullName); 21 else 22 configs = loader.getResources(fullName); 23 } catch (IOException x) { 24 fail(service, "Error locating configuration files", x); 25 } 26 } 27 while ((pending == null) || !pending.hasNext()) { 28 if (!configs.hasMoreElements()) { 29 return false; 30 } 31 pending = parse(service, configs.nextElement()); 32 } 33 nextName = pending.next(); 34 return true; 35 }
hasNextService()中,核心实现如下:
下面看一下next():
1 public S next() { 2 if (acc == null) { 3 return nextService(); 4 } else { 5 PrivilegedAction<S> action = new PrivilegedAction<S>() { 6 public S run() { return nextService(); } 7 }; 8 return AccessController.doPrivileged(action, acc); 9 } 10 } 11 12 private S nextService() { 13 if (!hasNextService()) 14 throw new NoSuchElementException(); 15 String cn = nextName; 16 nextName = null; 17 Class<?> c = null; 18 try { 19 c = Class.forName(cn, false, loader); 20 } catch (ClassNotFoundException x) { 21 fail(service, 22 "Provider " + cn + " not found"); 23 } 24 if (!service.isAssignableFrom(c)) { 25 fail(service, 26 "Provider " + cn + " not a subtype"); 27 } 28 try { 29 S p = service.cast(c.newInstance()); 30 providers.put(cn, p); 31 return p; 32 } catch (Throwable x) { 33 fail(service, 34 "Provider " + cn + " could not be instantiated", 35 x); 36 } 37 throw new Error(); // This cannot happen 38 }
nextService()中,核心实现如下:
再next()之后,拿到实现类实例后,就可以执行其具体的方法了。
五、缺点