dubbo源码—— spi机制
从一个小例子说起
@SPI
public interface Hello {
String sayHello(String name);
}
public class HelloImpl implements Hello {
@Override
public String sayHello(String name) {
return "hello " + name;
}
}
public static void main(String[] args) {
ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Hello.class);
Hello hello = (Hello) loader.getExtension("hello1");
System.out.println(hello.sayHello("lry"));
}
在resources下新建一个META-INF/dubbo文件夹,放入一个文件名为Hello接口的全路径名的文件,内容为 hello1 = com.lry.spi.test.HelloImpl。
这样子main函数就会打印hello lry
下面我们根据这个小例子来分析源码是如何实现的
源码分析
ps: 为了阅读方便,源码很多非关键处做了删减修改
getExtensionLoader
ExtensionLoader#getExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//type非null,type必须是接口,type必须有@SPI注解
//EXTENSION_LOADERS是map<Class,ExtensionLoader>
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (null == loader) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
ExtensionLoader#ExtensionLoader构造函数
private ExtensionLoader(Class<?> type) {
this.type = type;
//如果类型是ExtensionFactory,objectFactory 赋值为null,此时type是Hello,因此会走:后的逻辑
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
//getExtensionLoader又会调用到此构造函数,然后objectFactory 直接赋值为null
}
ExtensionLoader#getAdaptiveExtension
public T getAdaptiveExtension() {
//cachedAdaptiveInstance 缓存在类上加了@Adaptive的实例
//cachedAdaptiveInstance是一个Holder,持有一个对象,提供get,set方法
Object instance = cachedAdaptiveInstance.get();
if (null == instance) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (null == instance) {
//关键代码
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
}
}
}
return (T) instance;
}
ExtensionLoader#createAdaptiveExtension
private T createAdaptiveExtension() {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
}
ExtensionLoader#getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
//如果我们用@Adaptive标注了一个实现类,这个if就会直接返回,不会再走下面的逻辑生成代理类
if (null != cachedAdaptiveClass)
return cachedAdaptiveClass;
//如果我们没有@Adaptive标注一个实现类,那么会生成一个代理类作为Adaptive类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
ExtensionLoader#getExtensionClasses
private Map<String, Class<?>> getExtensionClasses() {
//cachedClasses也是一个Holder,持有一个Map<String, Class<?>>
Map<String, Class<?>> classes = cachedClasses.get();
if (null == classes) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (null == classes) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
ExtensionLoader#loadExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName();//缓存@SPI注解上的value值
Map<String, Class<?>> extensionClasses = new HashMap<>();
for (LoadingStrategy strategy : strategies) {
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
}
return extensionClasses;
}
初始化strategies
//加载策略
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
private static LoadingStrategy[] loadLoadingStrategies() {
//ServiceLoader 是jdk自带的spi,ServiceLoader可以加载META-INF/services下的文件
return StreamSupport.stream(ServiceLoader.load(LoadingStrategy.class).spliterator(), false).sorted().toArray(LoadingStrategy[]::new);
}
//LoadingStrategy一共有四个实现类,我列举两个
//ServiceLoadingStrategy, 加载路径为"META-INF/services/"
//DubboLoadingStrategy,加载路径为"META-INF/dubbo/"
ExtensionLoader#loadDirectory
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
//目录+接口名字的文件名 组合成一个完整文件名
String fileName = dir + type;
//通过类加载器和文件名拿到urls
Enumeration<java.net.URL> urls = getClassLoader(ExtensionLoader.class).getResources(fileName);
if (null != urls) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, loader, resourceURL, overridden, excludedPackages);
}
}
}
ExtensionLoader#loadResource
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader loader, java.net.URL resourceURL, boolean overridden, String[] excludedPackages) {
try (BufferedReader reader = new BufferedReader
(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
//加载spi配置文件配置的类
loadClass(extensionClasses, resourceURL,
Class.forName(line, true, loader),
name, overridden);
}
}
}
ExtensionLoader#loadClass
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name, boolean overridden) throws NoSuchMethodException {
//我们先要明白现在在加载那个文件,其实是在加载ExtensionFactory全路径名对应的那个文件,因为我们上面是从ExtensionLoader的构造函数那里一直看到这的,ExtensionFactory文件内容如下
//adaptive=com.lry.spi.AdaptiveExtensionFactory
//spi=com.lry.spi.SpiExtensionFactory
//省略springExtensionFactory
//遍历到文件第一行的时候,clazz是AdaptiveExtensionFactory,name=adaptive,因为这个类加了注解Adaptive,进入if缓存Adpative类
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz, overridden);//缓存Adaptive类,getAdaptiveExtension使用
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);//缓存wrapper类,到时候aop套娃会使用
} else {
cacheActivateClass(clazz, name);//缓存Activate信息,到时候getActivateExtension使用
cacheName(clazz, name);//cachedNames= Map<Class,name>
saveInExtensionClass(extensionClasses, clazz, name, overridden); // extensionClasses.put(name, clazz);
}
}
到此为止ExtensionFactory就加载完毕,并且所有相关实现类的信息都被缓存起来了
回退到 ExtensionLoader#getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
//如果我们用@Adaptive标注了一个实现类,这个if就会直接返回,不会再走下面的逻辑生成代理类
//因为AdaptiveExtensionFactory被@Adaptive标注,就会直接返回这个类
if (null != cachedAdaptiveClass)
return cachedAdaptiveClass;
//如果我们没有@Adaptive标注一个实现类,那么会生成一个代理类作为Adaptive类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
在回退到 ExtensionLoader#createAdaptiveExtension
private T createAdaptiveExtension() {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
}
newInstance调用AdaptiveExtensionFactory的默认构造函数
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
//getSupportedExtensions会返回spi=com.lry.spi.SpiExtensionFactory的name,即spi
for (String name : loader.getSupportedExtensions()) {
//getExtension("spi")就会得到SpiExtensionFactory的实例
list.add(loader.getExtension(name));
}
//factories 属性就是 SpiExtensionFactory和SpringExtensionFactory两个实例的list
factories = Collections.unmodifiableList(list);
}
ExtensionLoader#injectExtension
private T injectExtension(T instance) {
//此时objectFactory为null,无需DI
if (null == objectFactory)
return instance;
//其他DI逻辑,省略
}
到此为止getExtensionLoader这行代码就执行完毕了,代码确实很绕,特别是ExtensionLoader的构造函数
上面提到了@Adaptive和@Activate这两个注解,做个解释
@Adaptive注解可以放在类和方法上
- 放在方法上:代表dubbo会为此类的此方法代理,该类的其他方法没有加@Adaptive注解,直接抛异常
- 放在类上,代表dubbo不会做任何代理操作,getAdaptiveExtension的时候直接拿此类即可
@Activate注解可以放在类和方法上 ,放在方法上我暂时没有看到这种写法
他一般是放到类上,主要作用是构造一些拦截链,getActivateExtension可以根据条件筛选出合适的链出来。
这两个注解我后面会详细举例子说明的,不用担心。
getExtension
下面我们再看看getExtension(“hello1”)这行代码
ExtensinoLoader#getExtension(name,wrap)
public T getExtension(String name, boolean wrap) {
//如果我们调用传true,则拿默认的Extension,即 SPI注解的value值对应的那个实现类
if ("true".equals(name)) {
return getDefaultExtension();
}
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//http--->HttpProtocol对象
instance = createExtension(name, wrap);
holder.set(instance);
}
}
}
return (T) instance;
}
ExtensinoLoader#createExtension(name,wrap)
private Object createExtension(String name, boolean wrap) {
//getExtensionClasses现在才是去加载Hello接口的spi文件
Class<?> clazz = getExtensionClasses().get(name);
//clazz会是HelloImpl.class
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (null == instance) {
//直接反射
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//DI
injectExtension(instance);
//AOP,默认都是true
if (wrap) {
List<Class<?>> wrapperClassesList = new ArrayList<>();
//getExtensionClasses()会把wrapper类缓存到cachedWrapperClasses 里面
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
//看你的wapper有没有@Wrapper注解,如果没有直接套娃,否则需要根据mathcer,mismathces去匹配
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
//疯狂套娃,qos,filter,listener,httpProtocol(第一个套第二个,第二个套第三个....)
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
}
//调用LifeCycle 初始化方法
initExtension(instance);
return instance;
}
ExtensinoLoader#injectExtension
private T injectExtension(T instance) {
//此时objectFactory就不是null了,而是AdaptiveExtensionFactory
if (null == objectFactory)
return instance;
try {
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
//如果set方法加了DisableInject注解,不要DI
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
//拿到参数类型
Class<?> pt = method.getParameterTypes()[0];
//如果是java基础数据类型,不要DI
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
String property = getSetterProperty(method);
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
//把找出来的参数object赋值给set method那个属性
method.invoke(instance, object);
}
}
} catch (Exception e) {
log.error(e.getMessage());
}
return instance;
}
DI的应用实例:
public class HelloImpl implements Hello {
private XX xx; //XX接口必须有带Adaptive注解的方法(参数得有URL)或者有带Adaptive注解的实现类, 因为objectFactory.getExtension最后会调用到SpiExtensionFactory.getExtension方法,而方法里return loader.getAdaptiveExtension();只会找@Adaptive
//有set方法才可以DI
public void setHello(XX xx){
this.xx = xx;
}
@Override
public String sayHello(String name) {
return "hello " + name;
}
}
AOP/Wrapper的应用实例:
记得把HelloWrapper 的全路径名 加入到 hello接口对应的spi文件中
public class HelloWrapper implements Hello {
//套娃,getExtension("hello1")的时候HelloImpl实例会被HelloWrapper包一层
private Hello hello;
public HelloWrapper(Hello hello) {
this.hello = hello;
}
@Override
public String sayHello(String name) {
System.out.println("wrapper " +name);
return hello.sayHello(name);
}
}
getAdaptiveExtension
为了分析这行代码,我们需要把应用实例更新一下
hello1 = com.lry.spi.test.HelloImpl
hello2 = com.lry.spi.test.HelloImpl2
@SPI("hello2")
public interface Hello {
String sayHello(String name);
@Adaptive//如果没有@Adaptive标注一个类,而是放在方法上,那么此方法必须有URL,dubbo会生成一个代理类
String ada(URL url);
}
public class HelloImpl implements Hello {
@Override
public String sayHello(String name) {
return "hello " + name;
}
@Override
public String ada(URL url) {
return "ada";
}
}
public class HelloImpl2 implements Hello {
@Override
public String sayHello(String name) {
return "hello2 " + name;
}
@Override
public String ada(URL url) {
return "ada2";
}
}
public static void main(String[] args) {
ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Hello.class);
//getAdaptiveExtension 必须要有@adaptive的方法,方法必须带参数URL或者有实现类上加了@Adaptive
Hello h = (Hello) loader.getAdaptiveExtension();
URL url = new URL("http","localhost",8080);
// url = url.addParameter("hello","hello1");//这里和spi上的value一起起作用
// spi的默认值是hello2,因此打印ada2,如果上一行代码打开就会打印ada
System.out.println(h.ada(url));
}
getAdaptiveExtension代码最终调用到以下代码(之前的代码都分析过了)
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (null != cachedAdaptiveClass)
return cachedAdaptiveClass;
//如果我们没有@Adaptive标注一个实现类,那么会生成一个代理类作为Adaptive类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
现在我们没有用@Adpative放在实现类,只是放在了ada方法上,所以cachedAdaptiveClass是null,此时会走createAdaptiveExtensionClass,dubbo会动态为我们的Hello接口生成一个Hello$Adaptive类
ExtensionLoader#createAdaptiveExtensionClass
private Class<?> createAdaptiveExtensionClass() {
//动态生成代码,具体就不看,都是字符串拼装,没啥好看的
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = getClassLoader(ExtensionLoader.class);
//利用spi拿到一个编译器,最终会利用javasist编译源代码生成class文件
com.lry.spi.Compiler compiler = getExtensionLoader(com.lry.spi.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
动态生成的代码如下
package com.lry.spi.test;
import com.lry.spi.ExtensionLoader;
public class Hello$Adaptive implements com.lry.spi.test.Hello {
public java.lang.String sayHello(java.lang.String arg0) {
throw new UnsupportedOperationException("The method public abstract java.lang.String com.lry.spi.test.Hello.sayHello(java.lang.String) of interface com.lry.spi.test.Hello is not adaptive method!");
}
public java.lang.String ada(com.lry.spi.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.lry.spi.URL url = arg0;
//这里很关键啊,利用url的paramter参数定位到不同的实现类
String extName = url.getParameter("hello","hell2");
//如果调用的时候url不带param参数,extName 赋值为hello2
if(extName == null) throw new IllegalStateException("Failed to get extension (com.lry.spi.test.Hello) name from url (" + url.toString() + ") use keys([hello])");
com.lry.spi.test.Hello extension = (com.lry.spi.test.Hello)ExtensionLoader.getExtensionLoader(com.lry.spi.test.Hello.class).getExtension(extName);
return extension.ada(arg0);
}
}
如果加上下面这个类,getAdaptiveExtension拿的都是HelloImpl3 ,不会再被代理了
@Adaptive//直接拿的就是这个,不会代理
public class HelloImpl3 implements Hello {
@Override
public String sayHello(String name) {
return "hello3" +name;
}
@Override
public String ada(URL url) {
return "ada3";
}
}
getActivateExtension
myFilter1 = com.lry.spi.test.MyFilter1
myFilter2 = com.lry.spi.test.MyFilter2
myFilter3 = com.lry.spi.test.MyFilter3
@SPI
public interface MyFilter {
void filter();
}
@Activate(group = "provider",order =1)
public class MyFilter1 implements MyFilter {
@Override
public void filter() {
System.out.println("MyFilter1");
}
}
@Activate(group = "provider", order =2)
public class MyFilter2 implements MyFilter {
@Override
public void filter() {
System.out.println("MyFilter2");
}
}
@Activate(group = "consumer",order =3)
public class MyFilter3 implements MyFilter {
@Override
public void filter() {
System.out.println("MyFilter3");
}
}
public static void main(String[] args) {
ExtensionLoader<MyFilter> loader = ExtensionLoader.getExtensionLoader(MyFilter.class);
//拿文件里myFilter1对应的实现类和 组包含lry2的
System.out.println(loader.getActivateExtension(new URL("http","localhost",8080), new String[]{"myFilter1"}, "provider"));
}
ExtensionLoader#getActivateExtension
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
List<String> names =values==null?new ArrayList<>():Arrays.asList(values);
//加载一遍,已经加载了直接在缓存里了
getExtensionClasses();
for(Map.Entry<String,Object> entry: cachedActivates.entrySet()){
String name = entry.getKey();
Activate activate = (Activate) entry.getValue();
String[] activateGroup = activate.group();
String[] activateValue = activate.value();
//匹配group数组 和 url参数是不是可以匹配注解上的value数组
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
&& isActive(activateValue, url)) {
activateExtensions.add(getExtension(name));
}
}
activateExtensions.sort(ActivateComparator.COMPARATOR);
for (int i = 0; i < names.size(); i++) {
activateExtensions.add(getExtension(names.get(i)));
}
return activateExtensions;
}
private boolean isMatchGroup(String group, String[] groups) {
if (StringUtils.isEmpty(group)) {
return true;
}
if (groups != null && groups.length > 0) {
for (String g : groups) {
if (group.equals(g)) {
return true;
}
}
}
return false;
}
private boolean isActive(String[] keys, URL url) {
if (keys.length == 0) {
return true;
}
for (String key : keys) {
// @Active(value="key1:value1, key2:value2")
String keyValue = null;
if (key.contains(":")) {
String[] arr = key.split(":");
key = arr[0];
keyValue = arr[1];
}
for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
if ((k.equals(key) || k.endsWith("." + key))
&& ((keyValue != null && keyValue.equals(v)) || (keyValue == null && isNotEmpty(v)))) {
return true;
}
}
}
return false;
}
public static boolean isNotEmpty(String value) {
return !isEmpty(value);
}
public static boolean isEmpty(String value) {
return StringUtils.isEmpty(value)
|| "false".equalsIgnoreCase(value)
|| "0".equalsIgnoreCase(value)
|| "null".equalsIgnoreCase(value)
|| "N/A".equalsIgnoreCase(value);
}