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);
    }

代码下载

spi源码下载

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值