版本 2.7.4.1
spi是什么
来自百度:SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。
java spi
简单的代码示例
public interface People {
String getName();
}
public class Student implements People {
@Override
public String getName() {
System.out.println("student");
return "Student";
}
}
在META-INF/services/ 路径下面配置实现类
测试类:
public static void main(String[] args) {
ServiceLoader<People> serviceLoader = ServiceLoader.load(People.class);
Iterator<People> iterator = serviceLoader.iterator();
// resources/META-INF/services/ 源码已经写死了 META-INF/services/ 这个路径
//所以只能在这个路径下面编写接口实现类
while (iterator.hasNext()){
//获取接口实现类,做相应的操作
People next = iterator.next();
next.getName();
}
}
以上就是java spi的简单使用,可以实现对某个接口的动态扩展。这也是为什么要学习dubbo的spi,我们也可以对dubbo进行动态扩展。
dubbo的spi
dubbo的spi并不是使用原生的java spi机制,dubbo的spi机制更加丰富
入口
从ServiceConfig
里面的一句代码开始
//通过 ExtensionLoader 获取 Protocol 自适应扩展点
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
一步一步分析,先看 ExtensionLoader.getExtensionLoader(Protocol.class)
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//从map中拿 ExtensionLoader ,第一次没拿到
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
//没拿到就new 一个 ExtensionLoader 这个动作又做了好多事情,下面说
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
new ExtensionLoader<T>(type)
做了什么
private ExtensionLoader(Class<?> type) {
this.type = type;
//三目运算 这时候的 type=Protocol.class
//所以走的 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
//发现又回到 ExtensionLoader.getExtensionLoader(ExtensionFactory.class) 这个代码里面
//又掉绕这里, ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
//这句代码走到这里的时候type=ExtensionFactory 所以直接返回null
//当type=Protocol.class 的时候,objectFactory 是 ExtensionFactory 一个扩展点
//这个后面说
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
所以ExtensionLoader.getExtensionLoader(Protocol.class)
这个代码做的事情就是new一个 ExtensionLoader
对象,然后把type=Protocol.class放到 ExtensionLoader
的type属性,同时通过spi机制获取到ExtensionFactory
的扩展点放到objectFactory
这个属性。这两个属性都是为后面的 getAdaptiveExtension
方法服务的
``getAdaptiveExtension“ 做了什么,顾名思义,这个方法就是获取某个接口的自适应扩展点 dubbo的自适应扩展点是什么?下面源码有答案
public T getAdaptiveExtension() {
//cachedAdaptiveInstance 是缓存自适应扩展点
Object instance = cachedAdaptiveInstance.get();
//一开始缓存肯定为空
if (instance == null) {
//dubbo大量用到双重检查
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//缓存拿不到,看是真正创建自适应扩展点
//重点代码,下面解析
instance = createAdaptiveExtension();
//放到缓存里面
cachedAdaptiveInstance.set(instance);
}
}
}
}
return (T) instance;
}
createAdaptiveExtension
获取自适应扩展点
private T createAdaptiveExtension() {
try {
//一行代码做了很多事情
//1.先看 getAdaptiveExtensionClass 方法
//2.newInstance 这个不用看,就是通过反射new一个对象
//3.最后看 injectExtension 这个方法
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
}
}
getAdaptiveExtensionClass
获取自适应扩展点的class
private Class<?> getAdaptiveExtensionClass() {
//重点方法,去加载type这个接口下面所有的扩展点,放到缓存中
getExtensionClasses();
//如果上面的逻辑已经加载了自适应扩展点,就直接返回自适应扩展点的class
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//拼接代码生成自适应扩展点,然后默认使用 javassist 生成class
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getExtensionClasses
private Map<String, Class<?>> getExtensionClasses() {
//cachedClasses 缓存type下面的实现类
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//开始去load实现类
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
loadExtensionClasses
加载实现类
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName();
//存放实现类的容器
Map<String, Class<?>> extensionClasses = new HashMap<>();
// 有两个方法,另外一个应该是为了兼容alibaba相关的类,往下看loadDirectory方法
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
//.....省略去加载 "META-INF/services/" 和 "META-INF/dubbo/" 下面的实现类
//因为都是调用 loadDirectory 方法加载的
return extensionClasses;
}
loadDirectory
加载文件
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls;
//获取类加载器
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
//加载资源文件
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
//load文件下来之后开始解析
loadResource(extensionClasses, classLoader, resourceURL);
}
}
}
}
loadResource
解析实现类文件
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
//...省略大量逻辑,大概就是从文件加载出实现类
//这个文件跟java的spi文件不一样的是用键值对来表示
//加载文件,下面详解
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
/........
}
loadClass
加载类
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
//判断是否加 @Adaptive 如果是就放到 cachedAdaptiveClass 这个属性里
//所以在实现类中 加@Adaptive 注解就是自适应扩展点,自适应扩展点只能有一个
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz);
//判断是否包装类,通过 clazz.getConstructor(type); 这行代码判断
//如果不抛出异常,表示是包装类,否则不是
//包装类会缓存到 cachedWrapperClasses,后面会用到
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
} else {
//...省略名称获取
if (ArrayUtils.isNotEmpty(names)) {
//将 有@Activate 注解的类缓存到 cachedActivates 中
cacheActivateClass(clazz, names[0]);
for (String n : names) {
//cachedNames 缓存类跟名称的键值
cacheName(clazz, n);
//extensionClasses 缓存名称跟类的键值
saveInExtensionClass(extensionClasses, clazz, n);
}
}
}
}
从 loadClass
这个方法可以看出来,spi的扩展点有很多形式,比如加@Adaptive
@Active
包装类,普通类 但是 cachedClasses
这个只放普通和@Active
,@Adaptive
放到 cachedAdaptiveClass
包装类放到 cachedWrapperClasses
, @Active
还会放到 cachedActivates
现在回到 getAdaptiveExtensionClass
这个方法
private Class<?> getAdaptiveExtensionClass() {
//上面分析到这里,从配置文件中加载所有的实现类出来,放到本地缓存
getExtensionClasses();
//这个是缓存@Adaptive
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//如果所有实现类没有一个注有 @Adaptive 就走下面的逻辑,往下走
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class<?> createAdaptiveExtensionClass() {
//这个方法很长很复杂,不往下走了,总的就是判断type是否注有@Adaptive,没有就会抛异常
//有就会为这个方法生成具体的实现
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
//生成code之后,默认用javassist 生成class
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
以上就差不多完成从配置文件加载到本地缓存的过程,接下来看 injectExtension
方法做了什么 injectExtension
实现了dubbo的ioc的注入
private T injectExtension(T instance) {
//这个如果还记得的话,就是上文中实例ExtensionLoader的时候,new进去的
//所以这时候不为空
if (objectFactory == null) {
return instance;
}
try {
//遍历所有的方法
for (Method method : instance.getClass().getMethods()) {
//如果不是set方法就不做操作
if (!isSetter(method)) {
continue;
}
//如果有 @DisableInject 在方法上,说明不需要自动注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
//获取需要注入得calss
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
//获取需要注入的名称比如setXX 就是通过xx去容器获取
String property = getSetterProperty(method);
//objectFactory 的作用来了,就是从容器中获取依赖,下面讲
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
//调用反射,注入依赖
method.invoke(instance, object);
}
}
}
}
return instance;
}
完成注入之后就可以返回对象了,接下来看 objectFactory.getExtension
是怎么从容器中获取依赖的 在AdaptiveExtensionFactory
中实现。
public <T> T getExtension(Class<T> type, String name) {
//AdaptiveExtensionFactory 实例化的时候会通过spi机制加载
//ExtensionFactory 的普通实现类,目前包括
//SpiExtensionFactory 和 SpringExtensionFactory
//然后遍历 ExtensionFactory 实现类,执行 getExtension 获取依赖
//接下来分别看看这两个实现类的逻辑
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
SpringExtensionFactory
从spring容器中获取依赖
public <T> T getExtension(Class<T> type, String name) {
//......省略一下判断
//从spring上下文判断bean存不存在,如果存在getBean返回
for (ApplicationContext context : CONTEXTS) {
if (context.containsBean(name)) {
Object bean = context.getBean(name);
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
//.....
}
SpiExtensionFactory
通过dubbo的spi机制获取依赖,需要的依赖可能通过spi机制加载
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
//获取自适应扩展点
return loader.getAdaptiveExtension();
}
}
return null;
}
到这里dubbo的spi已经理的差不多了
为什么需要了解dubbo的spi
1、方便在使用dubbo的时候对dubbo进行扩展 2.dubbo很多地方都会用到spi,所以想要了解dubbo就先要知道dubbo的spi干了什么
未完待续………………