Java SPI
SPI全称(service provider interface),是JDK内置的一种服务提供发现机制,目前市面上有很多框架都是用它来做服务的扩展发现,大家耳熟能详的如JDBC、日志框架都有用到;
简单来说,它是一种动态替换发现的机制。举个简单的例子,如果我们定义了一个规范,需要第三方厂商去实现,那么对于我们应用方来说,只需要集成对应厂商的插件,既可以完成对应规范的实现机制。 形成一种插拔式的扩展手段。
规范总结
实现SPI,就需要按照SPI本身定义的规范来进行配置,SPI规范如下
-
需要在classpath下创建一个目录,该目录命名必须是:META-INF/services
-
在该目录下创建一个properties文件,该文件需要满足以下几个条件
a) 文件名必须是扩展的接口的全路径名称
b) 文件内部描述的是该扩展接口的所有实现类
c) 文件的编码格式是UTF-8
-
通过java.util.ServiceLoader的加载机制来发现
SPI 的缺点
- JDK标准的SPI会一次性加载实例化扩展点的所有实现,什么意思呢?就是如果你在META-INF/service下的文件里面加了N个实现类,那么JDK启动的时候都会一次性全部加载。那么如果有的扩展点实现初始化很耗时或者如果有些实现类并没有用到,那么会很浪费资源
- 如果扩展点加载失败,会导致调用方报错,而且这个错误很难定位到是这个原因
Dubbo SPI
Dubbo的SPI机制规范
大部分的思想都是和SPI是一样,只是下面两个地方有差异。
- 需要在resource目录下配置
META-INF/dubbo
或者META-INF/dubbo/internal
或者META-INF/services
,并基于SPI接口去创建一个文件 - 文件名称和接口名称保持一致,文件内容和SPI有差异,内容是KEY对应Value
public class MyProtocol implements Protocol {
@Override
public int getDefaultPort() {
return 1111;
}
public static void main(String[] args) {
Protocol protocol =
ExtensionLoader.
getExtensionLoader(Protocol.class).
getExtension("myProtocol");
System.out.println(protocol.getDefaultPort());
}
}
源码分析
@SPI("dubbo")//扩展点
public interface MonitorFactory {
@Adaptive("protocol")//适配器
Monitor getMonitor(URL url);
}
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {}
SPI 分析入口
public static void main(String[] args) {
//根据名称获得一个对应的扩展点
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
//获得自适应的扩展点
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
}
首先说的是 ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
源码之前需要了解一些反射的方法
public static void main(String[] args) throws NoSuchMethodException {
// 判断该类上是否有 Adaptive 注解,如果有返回 true,否则返回 false
System.out.println(MonitorFactory.class.isAnnotationPresent(Adaptive.class));
System.out.println(AdaptiveExtensionFactory.class.isAnnotationPresent(Adaptive.class));
System.out.println(Protocol.class.isAnnotationPresent(Adaptive.class));
// 判断 ProtocolFilterWrapper 类是否存在
// public ProtocolFilterWrapper(Protocol protocol) 的构造方法,
// 如果存在,返回该构造方法,不存在则报错
ProtocolFilterWrapper.class.getConstructor(Protocol.class);
}
ExtensionLoader.getExtensionLoader(Class<T> type)
是ExtensionLoader的一个静态方法,在该方法中会从缓存中获取 ExtensionLoader
对象,如果不存在则去调用私有的构造方法去 new ExtensionLoader ()
。
//初始化一个 ExtensionLoader,如果缓存有,则从缓存获得,如果缓存没有,则初始化一个 ExtensionLoader
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
// 如果 class 不是一个接口的话,则报错
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException
("Extension type(" + type +") is not extension, because WITHOUT @" +
SPI.class.getSimpleName() + " Annotation!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
// 调用私有的构造方法获得一个ExtensionLoader 实例
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
getAdaptiveExtension()
获得一个自适应扩展点,在该方法中,会通过双重校验从缓存中去取,取不出来会进行创建一个自适应的扩展点 createAdaptiveExtension()
-> getAdaptiveExtensionClass()
去获得一个扩展点的class对象。
getAdaptiveExtensionClass()
是比较核心的方法,在此处会根据 cachedAdaptiveClass
是否为空去选择直接返回还是走下面的方法。
//获得适配器的扩展点类,分两种,
//一种是自适应的类,一种是获得动态代理的
private Class<?> getAdaptiveExtensionClass() {
//加载所有路径下的扩展点的实现类
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//TODO 什么时候会不执行下面的方法,?
//TODO 当 clazz.isAnnotationPresent(Adaptive.class) 返回 true 时,
//TODO 即当 Adaptive 注解在类级别上的时候,会认为是一个自适应的扩展点,会将该类直接赋值到 cachedAdaptiveClass 中
//动态创建一个扩展点,即:type$Adpative 如:Protocol -> Protocol$Adpative
//TODO 什么时候会动态去创建扩展点,
//TODO 当 clazz.isAnnotationPresent(Adaptive.class) 返回 false 时,
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
此时进入 getExtensionClasses();
中,在方法中会去加载 /META-INF/
三个目录下的文件,即SPI的信息
//加载所有路径下的扩展点的实现类
//这里获得的扩展点不包括 clazz.isAnnotationPresent(Adaptive.class) 和 clazz.getConstructor(type);
// 即loadFile() 方法里进入catch 块中的内容:wrapper 和 类级别 Adaptive 注解
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 这里获得的扩展点不包括
// clazz.isAnnotationPresent(Adaptive.class) 和 clazz.getConstructor(type);
// 即只包含loadFile() 方法里进入catch 块中的内容:wrapper 和 类级别 Adaptive 注解
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
// 分别从三个路径下去加载SPI扩展点
// 此方法已经getExtensionClasses方法同步过。
private Map<String, Class<?>> loadExtensionClasses() {
//得到 SPI的注解
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
//获得@SPI("dubbo") 注解里的value 值,且该值只能为一个
String value = defaultAnnotation.value();
if (value != null && (value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException
("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
// dubbo 指定了三个路径,会从三个路径下去加载文件
// META-INF/services/internal/
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
// META-INF/dubbo/
loadFile(extensionClasses, DUBBO_DIRECTORY);
// META-INF/services/
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
在loadFile 方法中,会去解析三个目录下的spi文件信息
/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol
存放的是接口的实现类
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
该方法会去循环的去读取并实例化spi文件中的对象,并放到缓存中,而此时会有几处需要说下。
-
当该实现类上存在
Adaptive
时,会将实例的对象进行缓存起来,这个缓存的值,就是getAdaptiveExtensionClass()
中的 cachedAdaptiveClass ,所以在该方法里,只有当类上存在Adaptive
注解时,会将该类直接返回。如果该接口类型下所有的实现类的类上都没有Adaptive
注解的话,则走该方法下面的逻辑 -
当该实现类上没有存在
@Adaptive
该注解时,则判断clazz.getConstructor(type);
,即是否存在该实现类存在一个参数的构造方法,且这个构造方法的参数就是这个类的接口类。如:
Protocol.class -> public ProtocolFilterWrapper(Protocol protocol)
,如果存在则进行缓存到 cachedWrapperClasses ,该缓存的值会在
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
方法中用到,是对获得的实例对象进行包装,这块后面再说。如果不存在则报错,此时需要进入到catch 语句块中。
-
当进入到catch语句中时,说明此时的实现类既不是 类上存在
@Adaptive
注解,也不存在 持有接口类作为参数的构造方法。这些类进行存储到 extensionClasses,在上面提到的loadExtensionClasse()
方法中进行返回
这块代码比较长,就删除一些不重要的逻辑,需要看完整的可以到源码中查看 com.alibaba.dubbo.common.extension.ExtensionLoader#loadFile
//解析指定路径下的文件,获取对应的扩展点,通过反射的方式进行实例化以后,put到extensionClasses这个Map集合中
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
//加载对应的实现类,并且判断实现类必须是当前的加载的扩展点的实现
// @Adaptive如果是加在类上, 表示当前类是一个自定义的自适应扩展点 ,直接赋值
//如果是加在方法级别上,表示需要动态创建一个自适应扩展点,也就是Protocol$Adaptive
//TODO 获得该类上是否有 Adaptive 注解,存在返回 true
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException
("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else {
try {
//判断 ProtocolFilterWrapper
// 类是否存在 public ProtocolFilterWrapper(Protocol protocol) 的构造方法,
//判断 clazz 类 是否存在 public clazz(type t) 的构造方法,
// 如果存在,返回该构造方法,不存在则报错
clazz.getConstructor(type);
//如果没有Adaptive注解,则判断当前类是否带有参数是type类型的构造函数,
//如果没有,报错;如果有,则认为是wrapper类。这个wrapper实际上就是对扩展类进行装饰.
//可以在dubbo-rpc-api/internal下找到Protocol文件,发现Protocol配置了3个装饰
//分别是,filter/listener/mock. 所以Protocol这个实例来说,会增加对应的装饰器
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
//包装类:ProtocolFilterWrapper(ProtocolListenerWrapper(protocol))
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
clazz.getConstructor();
//.........省略部分...
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
// 这里的是对象的类上没有Adaptive 注解,在属性方法中,
// 也没有存放 public ProtocolFilterWrapper(Protocol protocol) 的构造方法,
// 这里进行缓存起来,在注入实例的时候用到 todo
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException
("Duplicate extension " + type.getName() + " name " + n + " on "
+ c.getName() + " and " + clazz.getName());
}
}
}
}
}
}
最后要说的就是 ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
根据上面所述,这块就好理解了
/**
* 返回指定名字的扩展。如果指定名字的扩展不存在,则抛异常 {@link IllegalStateException}.
*
* @param name
* @return
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 创建一个扩展点
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
// 获得指定名称的 扩展点,如果存在wrapper ,则会再此进行封装
@SuppressWarnings("unchecked")
private T createExtension(String name) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
//如果 存在 clazz.getConstructor(type); 不报错, 则 cachedWrapperClasses 不为空
//在 loadFile() 方法中 赋值
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T)
wrapperClass.getConstructor(type).newInstance(instance));
}
}
// 例如:如果是 DubboProtocol -> instance =
// ProtocolFilterWrapper(ProtocolListenerWrapper(DubboProtocol))
return instance;
} catch (Throwable t) {
throw new IllegalStateException
("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}