承接上篇, 本篇博文的主题就是认认真真捋一捋, 看一下 Dubbo是如何实现他的IOC / AOP / 以及Dubbo SPI这个拓展点的java
本篇的话整体上分红两部分进行展开程序员
以下代码是追踪的起点:apache
我也是看了好多遍才勉强将这个过程整理明白一些, 可是根据以往的经验来讲, 过一俩月以后我可能就会淡忘这个流程... 为了让本身一段时间后快速的回忆起来这个流程, 因此我要对本身说下面一段话编程
Dubbo的拓展点编码实现中, 会反反复复的出现下面这段代码api
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(XXX.class); extensionLoader.getExtension("XXX");
缓存先说这段代码在干什么? 其实上它就是在为 Dubbo原生的SPI接口, 或者是用户提供的SPI接口 结合SPI的配置文件中的配置, 找到这些SPI接口的实现类, 而且这个过程当中穿插Dubbo的IOC已经AOP机制安全
不得不服气, 这一段代码的实现, 由于这段代码设计不只仅能加载Dubbo提供的原生的SPI接口, 也能加载使用 用户自定义的SPI , 详细的过程在下文中展开, 妙!!! app
应该得, 隆重的介绍一下这个明星类 ExtensionLoader.javaide
从名字上看, ExtensionLoader , 见名知意, 拓展点的加载器, 那什么是Dubbo的拓展点呢? 拓展点就是Dubbo容许用户参与到Dubbo环境的初始化这个过程当中来, 容许用户定制Dubbo行为, 诸如 Dubbo的 SPI / IOC / AOP (上一篇博文主要的学习内容就是Dubbo的拓展点的使用)函数
见名知意: ExtensionLoader 拓展点的加载器, 就是使用这个封装类, 咱们能够加载Dubbo提供的拓展点, 说白了, 其实加载为SPI接口找到实现类, 以及完成这些实现类之间的 AOP加强 + IOC 依赖注入的过程
此外这个类颇有必要看, 为啥呢? 第一点就是说它的设计很巧妙, 代码的抽象和复用能力都很好, 第二点就是说, 咱们能够一睹大神们的风采, 若是 实现本身的SPI , 如何实现本身的IOC AOP
下面就是入口程序中的第一个方法, getExtensionLoader(Class<T> type)
很简单, 就是根据类型找到对应的 ExtensionLoader, 待会Dubbo就会为我添加进去SPI接口生成这样的 ExtensionLoader : org.apache.dubbo.common.extension.ExtensionLoader[com.changwu.ioc.api.PersonInterface]
固然Dubbo也有本身原生的ExtensionLoader
从个人入口程序来看, 很显然, 我传递进来的 type = PersonInterface
, 方法执行的逻辑以下
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 an interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); } //todo 这里体现了缓存机制, EXTENSION_LOADERS 其实就是 CurrentHashMap //todo EXTENSION_LOADERS 是 CurrentHashMap , 每一种interfaceType 都对应一个 ExtensionLoader , 可是这些 ExtensionLoader所有被维护在 这个EXTENSION_LOADER中 ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { 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; // 对于一个接口,好比PersonInterface接口,有两种实现类,一种就是咱们自定义的实现类,好比Student,还有一种就是代理类,对于代理类,能够由咱们本身实现,也可让Dubbo帮咱们实现,而代理类主要就是依赖注入时使用 // todo ExtensionFactory 是dubbo的拓展机制工厂, 它里面封装了 Dubbo的SPI拓展机制和Spring的拓展机制 // todo ExtensionLoader.getExtensionLoader(ExtensionFactory.class) ===> 获取 自适应的extension // || || || || || || // todo ExtensionLoader.getExtensionLoader(PersonInterface.class) ===> 昌武, 你获取的是: extension("human") // todo 你看这是否是挺清晰的 objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
在上面的构造方法中, 就有蹊跷了, 逻辑以下
我所说的有蹊跷的地方: 咱们原本不是前来建立PersionInterface 的ExtensionLoader吗? 怎么先建立 ExtensionFactory的 ExtensionLoader呢?
(由于在建立ExtensionFactory的 ExtensionLoader的过程当中会去加载Dubbo提供的其余的诸如SpiExtensionFactory这一类的实现, 这些默认的实现的做用就是辅助Dubbo再去解析用户提供的SPI实现体系)
下面看看这个 ExtensionFactory.class类
没错! 它被添加上了@SPI的注解, 说明和 咱们的PersonInterface同样, 是DubboSPI
那好吧, 既然Dubbo想先完成它的实例化, 就往下看, 我在博文开头就不停的说, Dubbo设计的很好, 这里不就递归调用getExtensionLoader(type= ExtensionFactory.class)了吗? 不出意外的话, 再一次的 进去构造方法, 而后在这个三元判断表达式中发现了 type == ExtensionFactory.class ? null : ExtensionLoader.getE... 前半部分是知足条件的, 而后设置objectFactory=null, 完成 ExtensionFactory的构造, 而后执行getAdaptiveExtension()
这个 getAdaptiveExtension()
一样须要好好的看看, 见名知意, 返回一个自适应的 Extension, 说白了就是返回Dubbo经过字符拼接出来的Extension类
下面看看这个 getAdaptiveExtension()
源码以下:
@SuppressWarnings("unchecked") public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError == null) { // todo 为了线程安全 , 使用了双重同步锁 synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { // todo 跟进去 instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); } } } } else { throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } } return (T) instance; }
着重跟进 instance = createAdaptiveExtension();
方法, 源码以下: 主要逻辑以下:
ExtensionClass 能够直白的理解成 SPI 接口的实现类, 或者是wrapper类
上面的代码中想要获取一个 AdaptiveExtensionClass()
那么问题来了, 从哪里获取呢? 跟进getAdaptiveExtensionClass()
没错就在下面的
private Class<?> getAdaptiveExtensionClass() { // todo 加载配置文件 getExtensionClasses(); // todo 在前一步加载配置文件时, 加载到了 AdaptiveExtensionFactory, 这里返回的就是 CachedAdaptiveClass if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } // 若是没有手动实现接口的代理类,那么Dubbo就会自动给你实现一个 return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
往下跟进getExtensionClasses();
下面的函数中维护着一个 cachedClasses
它是一个Map , key=String value= Class ; 说白了, 存放的就是从SPI配置文件中读取配置信息
// 实际上就是将配置中的 key=value 读取装在进map中 private Map<String, Class<?>> getExtensionClasses() { // todo cachedClasses是 ExtensionLoader的属性: Holder<Map<String, Class<?>>> cachedClasses // todo 用于存储提早约定好了存储在 类路径下的 METE-INF/services 以及dubbo原生提供的扩展点 // todo 一样是双重同步锁 + volatile 保证线程安全 Map<String, Class<?>> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { // todo 着重看这个函数 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; }
进行跟进loadExtensionClasses();
能够看到, Dubbo会按照约定读取下面几个配置文件中的配置信息, 下面我注释上的文件的全路径所对应的文件中会记录Dubbo原生的SPI的实现, 咱们也能遵循这个规则提供本身的实现类
好比随便查看一个配置文件
// synchronized in getExtensionClasses private Map<String, Class<?>> loadExtensionClasses() { cacheDefaultExtensionName(); Map<String, Class<?>> extensionClasses = new HashMap<>(); // todo 跟进这个 loadDirectory() 方法, 看看 // todo META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); // todo META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); // todo META-INF/dubbo/org.apache.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); //todo META-INF/dubbo/com.alibaba.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); //todo META-INF/services/org.apache.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); //todo META-INF/services/com.alibaba.dubbo.common.extension.ExtensionFactory loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; }
下面看一下处理的详细细节信息:
AdaptiveExtensionFactory.java
暂时缓存起来了SpiExtensionFactory,java
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } // todo 查看有没有标注 @Adaptive 注解, 若是标注有这个注解的话, 那么就将他暂时存放起来, 而不是执行下面的逻辑, 构造出对象来 // todo 昌武, 你看, 你在验证ioc时, 你提供的PersonInterface很显然是存在这个@Adaptive注解 ,他会在上面提到getAdaptiveClasss() 后 而后newInstance()建立实例 if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz); } else if (isWrapperClass(clazz)) { cacheWrapperClass(clazz); } else { clazz.getConstructor(); if (StringUtils.isEmpty(name)) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, name); } } } }
小结: 到这里基本上就到了Dubbo的底层确实会去读取配置文件, 根据他们的配置状况, 缓存在不一样容器中
好, 到这里上面所说的getExtensionClasses();
方法就说完了, 回到下面的方法中
获得了AdaptiveExtensionFactory
类以后, 接着就经过反射建立的它实例对象, 因此说, 咱们要去看他的构造方法, 以下:
getExtension(Class<T> type, String name)
又看到了这行代码ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
这行代码的执行流程其实已经说过了, 此次根据名称获取的 Extension是 SpiExtensionFactory, 并将它维护起来
新的问题就来了, 这个SPIExtensionFactory是谁呢? 有啥用呢 看下面, 说白了, 用它处理添加有Dubbo的SPI注解的接口, 而后尝试获取这些接口的 实现
构建方法执行完成了, 也就说明 AdaptiveExtension 建立完成了, 刚才所说的 createAdaptiveExtension
injectExtension其实就是回去作IOC / AOP 相关的操做, 如今咱们跟踪的实现类是 AdaptiveExtension
它没有依赖其余的属性, 可是我提供的PersonInterface依赖了, 因此说咱们暂时先不进如这个方法,稍后再进去查看他的实现
小结: 下图是咱们的启动类, 到目前为止, 咱们就看完了启动类的第一行代码作了什么, 那它主要是作了哪些事情呢?
下面就继续看这行extensionLoader.getExtension("human")
, 看他的返回值, 很明显, 就是要返回咱们须要的personInterface的实现类, 并在这个过程当中穿插这IOC和AOP的逻辑
回顾一下实验的环境, 从新整理一下思路: 咱们想获取出 key = human的 PersonInterface的实现类, 这实现类长下面这样:
public class Human implements PersonInterface { private PersonInterface personInterface; //todo 第一个关注点: 咱们的关注点就是说, Human 会帮咱们将哪个实现类当成入参注入进来呢? //todo 答案是 URL ,dubbo本身封装的URL, 统一资源定位符, dubbo 会解析入参位置的 url中封装的map //todo map中的key 与 PersonInteface中的使用 @Adaptive("person") 注解标记的value对应, 那么值就是将要注入的实际类型 //todo 第二个关注点: dubbo底层极可能是经过反射使用构造方法完成的属性注入 public void setPersonInterface(PersonInterface personInterface) { this.personInterface = personInterface; } @Override public String getName(URL url) { System.out.println("i am Human "); return "i am Human + " + personInterface.getName(url); } }
能够很直接的看到, 这个实现类实际上是依赖了一个 PersionInterface的属性,须要将这个属性注入给他, 因而问题来了, 注入的是谁呢? 下面继续往下拉看
进入下面的方法, 主要逻辑以下
咱们先看下: injectExtension(instance)的实现细节:
主要逻辑以下:
objectFactory.getExtension(pt,property)
上图中的主要目的就是完成依赖注入, 什么依赖注入呢? 就是在 Human.java中 依赖了一个PersonInterface类型的属性, Dubbo须要帮我填充上 , HumanInterface.java 中锁依赖的那个具体的实现类是谁呢? 就是在上面函数中的经过 objectFactory.getExtension(Class,name)
动态生成出来的
当咱们继续跟进这个getExtension(), 就会发现下面的现象, 看我在下图中标出来的绿色部分, 能够发现 , 他获取出来的 ExtensionLoader全称以下: 它就是Dubbo咱们生成出来的代理 ExtensionLoader
再进一步, 经过loader 获取出自适应的拓展类: getAdapativeExtension()
经过反编译看一下生成的Interface是谁, 能够看一下,它的实现, 这就是为何Dubbo经过URL就能知道该注入谁, 用谁取干活
先说啥是AOP, 就是面向切面编程, 其实说白了就是对现有的对象进行加强
Dubbo是怎么作的呢? 按照Dubbo的约定, 咱们的这样编码:即 经过继承+构造方法 实现AOP , 就像下面这样
Dubbo的底层实现: 处理AOP的逻辑在下面
Dubbo会从SPI配置文件中找到咱们添加就进去的Wrapperlei, 经过构造方法反射出他们的实例,, 重要的是将反射出来的这个实例替换成了原来未被加强的 对象, 就跟java的感受就像是升级版的静态代理同样
最后打一个小广告: 我是bloger 赐我白日梦, 本科大三在读, 热衷java研发, 指望有一份Java相关实习岗位的工做, 能够全职实习半年左右, 最理想城市是北京, 求大佬的内推哇