Dubbo源码解析之:SPI

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_38450840/article/details/90908823

 

写在前面:相信每个程序员都有一颗想看源码的心,但是经常被源码的复杂所吓倒,很多人都停留在想的层面,作者也是。最近刚好没有什么事情,就打算自己看一下源码,一直对Dubbo有好感,毕竟阿里出品必是精品,虽然Dubbo经历过停止代码维护更新的风波,好在后面又更新啦,文章使用的版本是2.5.4。GitHub链接https://github.com/apache/dubbo。选择tags的2.5.4下载。

下载以后可以导入到idea中,参考https://blog.csdn.net/fenfenguai/article/details/80611648https://blog.csdn.net/hpchenqi_16/article/details/80955546步骤不完全一样,建议直接下载,而不是克隆,克隆会克隆最新版本,另外下载下来的文件夹名可能不一样,请注意,弄好用idea打开就好。

下面结合自己看的一些博客和自己的理解和大家分享一下Dubbo中无处不在的SPI,为什么要先介绍SPI呢?因为在看 dubbo 源代码时如果没有了解扩展点机制,那么看到代码就是一片凌乱。源码中大量运用了SPI。写的不好的地方也请大家包容。声明:内容存在引用一些博主的博客如下(当然还有参考一些别的细节知识点,如设计模式等知识,这里不再列出)。

https://juejin.im/post/5c0cd73b5188256dd7744858

https://www.jianshu.com/p/99f568df0f05

一、首先我们对 Java SPI(Service Provider Interface)介绍进而引入Dubbo SPI.

这里先给出Java SPI的简单实现,后面再做分析。

首先定义一个接口,再定义两个实现类

public interface IService {
    public String sayHello();
	public String getScheme();
}
public class HBaseServiceImpl implements IService {
 
	@Override
    public String sayHello() {
		return "Hello HBase!!";
	}
 
	@Override
	public String getScheme() {
		return "HBase";
	}
 
}
public class HDFSServiceImpl implements IService {
 
	@Override
    public String sayHello() {
		return "Hello HDFS!!";
	}
 
	@Override
	public String getScheme() {
		return "hdfs";
	}
}
public class ServiceLoaderTest {
	public static void main(String[] args) {
		ServiceLoader<IService> serviceLoader = ServiceLoader.load(IService.class);
		for (IService iService : serviceLoader) {
			System.out.println(iService.getScheme()+"  =  "+iService.sayHello());
		}

	}
 
这里做些解释:java.util.ServiceLoader这个类来从配置文件中加载子类或者接口的实现类。
主要是从META-INF/services这个目录下的配置文件加载给定接口或者基类的实现,
ServiceLoader会根据给定的类的full name来在META-INF/services下面找对应的文件,
在这个文件中定义了所有这个类的子类或者接口的实现类,返回一个实例。

 尝试一下,用#注释和不注释运行结果的区别。

//可以看到ServiceLoader可以根据IService把定义的两个实现类找出来,返回一个ServiceLoader的实现,
//而ServiceLoader实现了Iterable接口,所以可以通过ServiceLoader来遍历所有在配置文件中定义的类的实例。
//从使用层面来说,就是运行时,动态给接口添加实现类.其实这有有点像IoC的思想,将装配的控制权移到程序之外.通过改变配置文件,我们就能动态的改变一个接口的实现类.
//SPI 就是这样一种基于接口编程+策略模式+配置文件,同时可供使用者根据自己的实际需要启用/替换模块具体实现的方案。

//那么很容易想到一个问题,比如我想新增一个接口的实现类XXXServiceImpl,这样的话光改配置文件也还是不行,还要预先包里面就有这个实现类才行啊.
//这就要用到javassist,也就是动态字节码技术.这样可以在运行时动态生成Java类,就不存在要预先把接口的实现类先在包里放好

//事实上我就算不用spi,我用spring的ioc也能通过配置文件或者注解动态的注入不同的实现类啊
//在dubbo的设计中,就不想强依赖Spring的IoC容器,但是自已造一个小的IoC容器,也觉得有点过度设计.另外dubbo是不需要依赖任何第三方库的,引用官方文档原话如下
//理论上 Dubbo 可以只依赖 JDK,不依赖于任何三方库运行,只需配置使用 JDK 相关实现策略

Dubbo SPI 的改进点

以下内容摘录自 https://dubbo.gitbooks.io/dubbo-dev-book/SPI.html
Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。 JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点

上面这段话,暂时看不懂也没关系,我们先放在这,到时候回过头来看也是可以的。

在 Dubbo 中,如果某个 interface 接口标记了 @SPI 注解,那么我们认为它是 Dubbo 中的一个扩展点。扩展点是 Dubbo SPI 的核心。

Dubbo SPI 机制详解

Dubbo 扩展点的加载

上文说到Java SPI有 /META-INF/services 这样一个目录。在这个目录下有一个以接口命名的文件,文件的内容为接口具体实现类的全限定名。在 Dubbo 中我们也能找到类似的设计。

  • META-INF/services/(兼容JAVA SPI)
  • META-INF/dubbo/(自定义扩展点实现)
  • META-INF/dubbo/internal/(Dubbo内部扩展点实现)

非常好~我们现在已经知道了从哪里加载扩展点了,再回忆一下,JAVA SPI是如何加载的。

ServiceLoader<DubboService> spiLoader = ServiceLoader.load(XXX.class);

类似的,在 Dubbo 中也有这样一个用于加载扩展点的类 ExtensionLoader,这个是不是跟 SPI 机制非常像,只是 java spi 机制是 java 后台帮你实现读取文件并对接具体的实现类,而 dubbo 是自己去读文件。我们先来看一段简短的代码。

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

在 Dubbo 的实现里面用到了大量类似的代码片段,我们只需要提供一个 type ,即可获取该 type 的自适应(关于自适应的理解在后文会提到)扩展类。在获取对应自适应扩展类时,我们首先获取该类型的 ExtensionLoader。看到这里我们应该下意识的感觉到对于每个 type 来说,都应该有一个对应的 ExtensionLoader 对象。我们先来看看 ExtensionLoader 是如何获取的。很自然想到应该是getXXX方法。

getExtensionLoader()

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null)//拓展点类型非空判断
            throw new IllegalArgumentException("Extension type == null");
        if (!type.isInterface()) {//拓展点类型只能是接口
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
        //拓展点类型(接口)是否使用@SPI("xxx")注解标识
        if (!withExtensionAnnotation(type)) {//需要添加spi注解,否则抛异常
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }

        //对于每一个type拓展,都会有且只有一个ExtensionLoader与其对应
        //从缓存EXTENSION_LOADERS中根据type获取ExtensionLoader,
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);//ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
        if (loader == null) {                                                        //EXTENSION_LOADERS 为一个 ConcurrentMap集合,key 为 Class 对象,value 为ExtenLoader 对象
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));//如果不存在则新建后加入缓存
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);//把刚新建后加入缓存的,根据type取出。
        }
        return loader;//返回type所对应的ExtensionLoader
    }


private ExtensionLoader(Class<?> type) {
        this.type = type;
        //这里会存在递归调用,如果type是ExtensionFactory.class则objectFactory为null,其他类型均为AdaptiveExtensionFactory
        //AdaptiveExtensionFactory的factories中有SpiExtensionFactory,SpringExtensionFactory
        //getAdaptiveExtension()这个是获取一个拓展装饰类对象.                                                                //3.然后进入到getAdaptiveExtension方法中
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
        //这里的 objectFactory 获取到了 ExtensionFactory 的扩展自适应类(即AdaptiveExtensionFactory)。
    }

 上面注释写到:AdaptiveExtensionFactory类的factories中有SpiExtensionFactory,SpringExtensionFactory实际上这个也是通过SPI实现的。具体见下图

ExtensionFactory有两个实现类,

 

dubbo在设计的时候设计了这两种方式,但是截止2.5.4版本,SpringExtensionFactory的方式尚未发现使用,可能像Java的保留字一样,给以后埋下伏笔.

 

//1.总结一下getExtensionLoader方法,主要还是根据传入type获取对应的ExtensionLoader对象。
//前面三个if判断,要求传入类型非空,且用@SPI("xxx")注解标识的接口。
//然后根据 type 从 EXTENSION_LOADERS 集合中获取 loader ,如果返回的值为 null 则新建一个 ExtensionLoader 对象。2.然后进到此类的构造方法中

getAdaptiveExtension()

public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();//cachedAdaptiveInstance用于缓存自适应扩展类实例
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {//下面用到了double-checked locking代码
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            instance = createAdaptiveExtension();//4.然后进入到createAdaptiveExtension方法
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }

        return (T) instance;
    }
//getAdaptiveExtension()方法用于获取当前自适应扩展类实例,首先会从 cachedAdaptiveInstance 对象中获取,
//如果值为 null 同时 createAdaptiveInstanceError 为空,则调用 createAdaptiveExtension 方法创建扩展类实例。创建完后更新 cachedAdaptiveInstance 。

createAdaptiveExtension()

private T createAdaptiveExtension() {//这里有两个方法值得我们关注,injectExtension() 和 getAdaptiveExtensionClass()。
        // injectExtension() 看名字像是一个实现了注入功能的方法,而 getAdaptiveExtensionClass() 则用于获取具体的自适应扩展类。我们依次看下这两个方法。
        try {//5.进入getAdaptiveExtensionClass方法中
             //9.进入injectExtension()
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
        }
    }

 

getAdaptiveExtensionClass()

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();//将扩展点实现加载到了缓存中 6.进入到getExtensionClasses()
        if (cachedAdaptiveClass != null) {//判断缓存对象 cachedAdaptiveClass 是否会空,cachedAdaptiveClass 是什么时候被初始化的呢?
            return cachedAdaptiveClass;   //答:是在loadFile()方法中,具体说来从getExtensionClasses方法一直追溯到loadFile中可以看到:
                                          //如果发现当前 clazz 包含 @Adaptive 注解,则将当前 clazz 作为缓存自适应类保存。
                                          //例如在 AdaptiveExtensionFactory 类中就有这么用,我们会将 AdaptiveExtensionFactory 类作为 ExtensionFactory 类型的自适应类缓存起来。
        }               //@Adaptive
                        //public class AdaptiveExtensionFactory implements ExtensionFactory
        return cachedAdaptiveClass = createAdaptiveExtensionClass();//动态生成一个自适应扩展类。
    }

getAdaptiveExtensionClass() 方法。首先调用 getExtensionClasses() 方法,如果 cachedAdaptiveClass() 不为 null 则返回,如果为 null 则调用 createAdaptiveExtensionClass() 方法。依次看下这两个方法。

getExtensionClasses()

private Map<String, Class<?>> getExtensionClasses() {//内容比较简单,就是获取classes
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    classes = loadExtensionClasses();//7.进入loadExtensionClasses()
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

内容比较简单,我们直接看

loadExtensionClasses() 

private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            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];
            }
        }
        //绕来绕去这么久,终于要进入主题了。为什么说进入主题了呢?看看这几个变量的值

        //DUBBO_INTERNAL_DIRECTORY:META-INF/dubbo/internal/
        //DUBBO_DIRECTORY:META-INF/dubbo/
        //SERVICES_DIRECTORY:META-INF/services/
        //熟悉的配方熟悉的料。。没错了,我们马上就要开始读取这三个目录下的文件,然后开始加载我们的扩展点了。
        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);//8.进入loadFile()
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

接着看看loadFIle()

private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
        String fileName = dir + type.getName();
        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 url = urls.nextElement();
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                        try {
                            String line = null;
                            while ((line = reader.readLine()) != null) {
                                final int ci = line.indexOf('#');//#对应注释位置,剔除存在的注释
                                if (ci >= 0) line = line.substring(0, ci);
                                line = line.trim();
                                if (line.length() > 0) {
                                    try {
                                        String name = null;
                                        int i = line.indexOf('=');//文件中的内容以 key=value 的形式保存,拆分 key 和 vlaue
                                        if (i > 0) {
                                            name = line.substring(0, i).trim();
                                            line = line.substring(i + 1).trim();
                                        }
                                        if (line.length() > 0) {
                                            Class<?> clazz = Class.forName(line, true, classLoader);
                                            if (!type.isAssignableFrom(clazz)) {// 用于判断 class 是不是 type 接口的实现类
                                                throw new IllegalStateException("Error when load extension class(interface: " +
                                                        type + ", class line: " + clazz.getName() + "), class "
                                                        + clazz.getName() + "is not subtype of interface.");
                                            }
                                            if (clazz.isAnnotationPresent(Adaptive.class)) { // 如果当前 class 被 @Adaptive 注解标记,更新 cachedAdaptiveClass 缓存对象
                                                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 {
                                                    clazz.getConstructor(type);//这个方法比较简单,尝试获取 clazz 中以 type 为参数的构造方法,如果可以获取到,则认为 clazz 则是当前 type 类的包装类。
                                                    Set<Class<?>> wrappers = cachedWrapperClasses;// A 类有一个以 A 为参数的构造方法,我们称它为复制构造方法。有这样构造方法的类在 Dubbo 中我们称它为 Wrapper 类。
                                                    if (wrappers == null) {
                                                        cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                                                        wrappers = cachedWrapperClasses;
                                                    }
                                                    wrappers.add(clazz);
                                                } catch (NoSuchMethodException e) {
                                                    clazz.getConstructor();
                                                    // 如果 name 为空,调用 findAnnotationName() 方法。如果当前类有 @Extension 注解,直接返回 @Extension 注解value;
                                                    // 若没有 @Extension 注解,但是类名类似 xxxType(Type 代表 type 的类名),返回值为小写的 xxx
                                                    if (name == null || name.length() == 0) {
                                                        name = findAnnotationName(clazz);
                                                        if (name == null || name.length() == 0) {
                                                            if (clazz.getSimpleName().length() > type.getSimpleName().length()
                                                                    && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                                                                name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                                                            } else {
                                                                throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                                                            }
                                                        }
                                                    }
                                                    String[] names = NAME_SEPARATOR.split(name);
                                                    if (names != null && names.length > 0) {
                                                        // @Activate 注解用于配置扩展被自动激活条件
                                                        // 如果当前 class 包含 @Activate ,加入到缓存中
                                                        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) {
                                                                extensionClasses.put(n, clazz);
                                                            } else if (c != clazz) {
                                                                throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    } catch (Throwable t) {
                                        IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
                                        exceptions.put(line, e);
                                    }
                                }
                            } // end of while read lines
                        } finally {
                            reader.close();
                        }
                    } catch (Throwable t) {
                        logger.error("Exception when load extension class(interface: " +
                                type + ", class file: " + url + ") in " + url, t);
                    }
                } // end of while urls
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

比较长,具体干了这几件事情。

/*
拼接生成文件名:dir + type,读取该文件
读取文件内容,将文件内容拆分为 name 和 class 字符串
如果 clazz 类中包含 @Adaptive 注解,将其加入到 cachedAdaptiveClass 缓存中
如果 clazz 类中为包装类,添加到 wrappers 中
如果文件不为 key=class 形式,会尝试通过 @Extension 注解获取 name
如果 clazz 包含 @Activate 注解(兼容 com.alibaba.dubbo.common.extension.Activate 注解),将其添加到 cachedActivates 缓存中
最后以 name 为 key ,clazz 为 vlaue,将其添加到 extensionClasses 集合中并返回
*/

再回到getAdaptiveExtensionClass()方法里,

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();//将扩展点实现加载到了缓存中 6.进入到getExtensionClasses()
        if (cachedAdaptiveClass != null) {//判断缓存对象 cachedAdaptiveClass 是否会空,cachedAdaptiveClass 是什么时候被初始化的呢?
            return cachedAdaptiveClass;   //答:是在loadFile()方法中,具体说来从getExtensionClasses方法一直追溯到loadFile中可以看到:
                                          //如果发现当前 clazz 包含 @Adaptive 注解,则将当前 clazz 作为缓存自适应类保存。
                                          //例如在 AdaptiveExtensionFactory 类中就有这么用,我们会将 AdaptiveExtensionFactory 类作为 ExtensionFactory 类型的自适应类缓存起来。
        }               //@Adaptive
                        //public class AdaptiveExtensionFactory implements ExtensionFactory
        return cachedAdaptiveClass = createAdaptiveExtensionClass();//动态生成一个自适应扩展类。
    }

到这里,我们已经分析完了,getExtensionClasses 方法,并且已经将扩展点实现加载到了缓存中。然后的if判断,代码里已经做了注释不再赘述,给出loadFile方法中将 包含 @Adaptive 注解,的 clazz 作为缓存自适应类保存的关键代码。

if (clazz.isAnnotationPresent(Adaptive.class)) { // 如果当前 class 被 @Adaptive 注解标记,更新 cachedAdaptiveClass 缓存对象
                                                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 {...}

我们继续分析该方法的后部分。如果 cachedAdaptiveClass 为 null,则会调用 createAdaptiveExtensionClass() 方法动态生成一个自适应扩展类。这一段代码在本次分享中不打算重点叙述,具体参见https://blog.csdn.net/chaoyuehu/article/details/79510193可以简单的理解为 dubbo 帮我生成了一个自适应类。

我们再回到

createAdaptiveExtension()

private T createAdaptiveExtension() {//这里有两个方法值得我们关注,injectExtension() 和 getAdaptiveExtensionClass()。
        // injectExtension() 看名字像是一个实现了注入功能的方法,而 getAdaptiveExtensionClass() 则用于获取具体的自适应扩展类。我们依次看下这两个方法。
        try {//5.进入getAdaptiveExtensionClass方法中
             //9.进入injectExtension()
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
        }
    }

费了很大力气说完了,getAdaptiveExtensionClass()方法,后面调用了newInstance方法进行实例化,现在我们看到

injectExtension()

private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    if (method.getName().startsWith("set")
                            && method.getParameterTypes().length == 1
                            && Modifier.isPublic(method.getModifiers())) {
                        //获取 method 第一个参数的类型
                        Class<?> pt = method.getParameterTypes()[0];
                        try {
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            Object object = objectFactory.getExtension(pt, property);//objectFactory 在 ExtensionLoader 的构造方法中被初始化,在这里获取到自适应扩展类为 AdaptiveExtensionFactory。
                            if (object != null) {//执行 AdaptiveExtensionFactory.getExtension()
                                method.invoke(instance, object);//会用反射机制将其注入对应类
                            }
                        } catch (Exception e) {
                            logger.error("fail to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }
// 遍历当前实例的 set 方法,以 set 方法第四位开始至末尾的字符串为关键字,尝试通过 objectFactory 来获取对应的 扩展类实现。
// 如果存在对应扩展类,!!!通过反射注入!!!到当前实例中。
// 这个方法相当于完成了一个简单的依赖注入功能,我们常说 Dubbo 中的 IOC 实际上也是在这里体现的。IOC后面具体再说。我们先看

getExtension()

首先说明一点:扩展类会在调用 getExtension() 方法时才会被实例化!!!

/**
     * 返回指定名字的扩展。如果指定名字的扩展不存在,则抛异常 {@link IllegalStateException}.
     *
     * @param name
     * @return
     */
    @SuppressWarnings("unchecked")
    public T getExtension(String name) {//缓存拓展点对象     扩展类会在调用 getExtension() 方法时才会被实例化。
        if (name == null || name.length() == 0)             //具体说来是在createExtension方法里面用newInstance初始化,具体进方法里看看。
            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(); //double-checked locking
        if (instance == null) {         //doubleCheck Singleton 重排序Holder类holder.get()得到的value使用volatile修饰的。
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);//进入到createExtension() 方法。
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

上面的逻辑比较简单,这里也不赘述了,直接看 createExtension() 方法。

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);//调用 injectExtension() 方法进行依赖注入
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;//还记得 cachedWrapperClasses 在什么地方被初始化的吗?
            if (wrapperClasses != null && wrapperClasses.size() > 0) {//在loadFile() 方法中介绍过:在加载扩展点时,我们将对应 type 的包装类缓存起来
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
        

    }

 

getExtensionClasses() 方法在前文已经分析过了,但是需要注意的是:getExtensionClasses 返回给我们的不过是使用 Class.forName() 加载过的类而已,充其量执行了里面的静态代码段,而并非得到了真正的实例。真正的实例对象仍需要调用 class.newInstance() 方法才能获取。了解了这些之后我们继续看,我们通过 getExtensionClasses() 尝试获取系统已经加载的 class 对象,通过 class 对象再去扩展实例缓存中取。如果扩展实例为 null,调用 newInstance() 方法初始化实例,并放到 EXTENSION_INSTANCES 缓存中。之后再调用 injectExtension() 方法进行依赖注入。最后一段涉及到包装类的用法,下面我们进行介绍。我们先了解下 Dubbo 中 wrapper 类的定义。 举个例子:

class A {
    private A a;
    public A(A a){
        this.a = a;
    }
}

我们可以看到 A 类有一个以 A 为参数的构造方法,我们称它为复制构造方法。有这样构造方法的类在 Dubbo 中我们称它为 Wrapper 类。判断一个类是否为Wrapper 类也比较简单,尝试获取 clazz 中以 type 为参数的构造方法,如果可以获取到,则认为 clazz 则是当前 type 类的包装类。

还记得 cachedWrapperClasses 在什么地方被初始化的吗?
我们会发现在loadFile() 方法中介绍过:在加载扩展点时,我们将对应 type 的包装类缓存起来。代码如下:

clazz.getConstructor(type);//这个方法比较简单,尝试获取 clazz 中以 type 为参数的构造方法,如果可以获取到,则认为 clazz 则是当前 type 类的包装类。
Set<Class<?>> wrappers = cachedWrapperClasses;// A 类有一个以 A 为参数的构造方法,我们称它为复制构造方法。有这样构造方法的类在 Dubbo 中我们称它为 Wrapper 类。
if (wrappers == null) {
        cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
        wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);

结合上面的代码,我们会发现在加载扩展点时,我们将对应 type 的包装类缓存起来。

为了更好的理解createExtension() 方法里关于wrapperClasses的代码,我们假设当前 type 值为 Protocol.class ,
我们可以在 org.apache.dubbo.rpc.Protocol 文件中找到 Protocol 接口的包装类 ProtocolFilterWrapper 和 ProtocolListenerWrapper,
        // 通过loadFile方法,他们会依次被添加到 cachedWrapperClasses 集合中。
        // 再说回到这段代码
         /*for (Class<?> wrapperClass : wrapperClasses) {
            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
        }*/
        // 即,依次遍历 cachedWrapperClasses(上面已经用Set<Class<?>> wrapperClasses = cachedWrapperClasses进行了赋值) 集合,
        // 比如第一次取到的是 ProtocolFilterWrapper 类,
        // 调用.newInstance(instance)创建对象,则会调用 ProtocolFilterWrapper 的复制构造方法将 instance 包装起来。创建完 ProtocolFilterWrapper 对象实例后,
        // 调用 injectExtension() 进行依赖注入。此时 instance 已经为 ProtocolFilterWrapper 的实例,继续循环,
        // 会将 ProtocolFilterWrapper 类包装在 ProtocolListenerWrapper 类中。因此我们最后返回的是一个 ProtocolListenerWrapper 实例。
        // 最后调用时,仍会通过一层一层的调用,最后调用原始 instance 的方法。
        // 这里的包装类有点类似 AOP 思想,我们可以通过一层一层的包装,在调用扩展实现之前添加一些日志打印、监控等自定义的操作。

Dubbo 中的 IOC 机制

上文中我们已经讨论过 Dubbo 中利用反射机制实现一个类 IOC 功能。我们再回顾一下 injectExtension() 方法,仔细的来看看 Dubbo 中 IOC 功能的实现。

dubbo的IOC具体实现在:T injectExtension(T instance)方法中。该方法只在三个地方被使用:

1 createAdaptiveExtension()
2 --injectExtension((T) getAdaptiveExtensionClass().newInstance()) //为创建好的AdaptiveExtensionClass实例进行属性注入
3 
4 createExtension(String name)
5 --injectExtension(instance) //为创建好的Extension实例进行属性注入
6 --injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)) //为创建好的wrapperClass实例进行属性注入
injectExtension整个方法的作用就是通过instance对象实例的setter方法为instance的属性赋值,完成setter注入,即IOC的最经典的注入方式。

createExtension()方法中有这样一段代码。
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));


private T injectExtension(T instance) {
    // ···
    Class<?> pt = method.getParameterTypes()[0];
    try {
        String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
        Object object = objectFactory.getExtension(pt, property);
        if (object != null) {//执行instance.method(object)方法,这里就是执行instance的setter方法,进行setter注入
            method.invoke(instance, object);
        }
    }
    // ···
}

public class StubProxyFactoryWrapper implements ProxyFactory {
    // ...
    private Protocol protocol;
    public StubProxyFactoryWrapper(ProxyFactory proxyFactory) {//可以看出是复制构造函数,因此是ProxyFactory的Wrapper类
        this.proxyFactory = proxyFactory;
    }
    public void setProtocol(Protocol protocol) {
        this.protocol = protocol;
    }
    //...
}

在上文中我们已经讲过 wrapper 类,我们举个例子说明一下。 比如我们当前的ProxyFactory的 wrapperClass 类为 StubProxyFactoryWrapper,那么代码执行逻辑大致如下所示:

1 创建 StubProxyFactoryWrapper 实例;

2 获取上述实例作为 injectExtension() 的参数,执行;

3 injectExtension() 方法循环遍历到 StubProxyFactoryWrapper 的 setProtocol()方法(此时 pt=Protocol.class,property=protocol),执行 objectFactory.getExtension(pt,property) 方法。objectFactory 在 ExtensionLoader 的构造方法中被初始化,在这里获取到自适应扩展类为 AdaptiveExtensionFactory。

4 执行 AdaptiveExtensionFactory.getExtension()。AdaptiveExtensionFactory 类中有一个集合变量 factories。 factories 在 AdaptiveExtensionFactory 的构造方法中被初始化,包含了两个工厂类:SpiExtensionFactory、SpringExtensionFactory。执行 AdaptiveExtensionFactory 类的 getExtension() 方法会依次调用 SpiExtensionFactory 和 SpringExtensionFactory 类的 getExtension() 方法。

5 执行 SpiExtensionFactory 的 getExtension() 方法。上面有说到此时的 type=Procotol.class,property=protocol,从下面的代码我们可以发现 Protocol 是一个接口类,同时标注了 @SPI 注解,此时会获取 Protocol 类型的 ExtensionLoader 对象,最后又去调用 loader 的 getAdaptiveExtension() 方法。最终获取到的自适应类为 Protocol$Adaptive 动态类。

6 objectFactory.getExtension(pt, property); 最后得到的类为 Protocol$Adaptive 类,最后利用反射机制将其注入到 StubProxyFactoryWrapper 实例中。 反射机制参考https://blog.csdn.net/Mr_Tim/article/details/51594717

 public <T> T getExtension(Class<T> type, String name) {// Protocol 是一个接口类,同时标注了 @SPI 注解,此时会获取 Protocol 类型的 ExtensionLoader 对象,
        // 最后又去调用 loader 的 getAdaptiveExtension() 方法。最终获取到的自适应类为 Protocol$Adaptive 动态类。
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (loader.getSupportedExtensions().size() > 0) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

END

在最后,我们再回顾下开头关于 Dubbo SPI 基于 JAVA SPI 改进的那段话:

Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。 JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点

总结如下:

//    在 Dubbo 中,如果某个 interface 接口标记了 @SPI 注解,那么我们认为它是 Dubbo 中的一个扩展点。
//    Dubbo SPI 在加载扩展点,会以 key-value 的形式将扩展类保存在缓存中,但此时的扩展类只是调用 Class.forName() 加载的类,并没有实例化。扩展类会在调用 getExtension() 方法时被实例化。
//    Dubbo 通过工厂模式和反射机制实现了依赖注入功能。
//    Dubbo 中通过包装类实现了 AOP 机制,方便我们添加监控和打印日志。
展开阅读全文

没有更多推荐了,返回首页