Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力。这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo 提供的丰富服务治理能力,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。同时 Dubbo 是高度可扩展的,用户几乎可以在任意功能点去定制自己的实现,以改变框架的默认行为来满足自己的业务需求

  本文主要给大家讲解下Dubbo的扩展点原理。

一、SPI介绍

  JDK中的SPI(Service Provider Interface)提供了一种基于接口的扩展机制,主要实现步骤如下:

  • 定义一个接口作为一个标准。
  • 在实现扩展点的工程中创建一个META-INF/services目录,以定义的接口的全类名作为文件名创建一个文件名,将实现类的全类名写入到文件中。
  • 在需要使用扩展点的地方调用java.util.ServiceLoader#load(java.lang.Class)方法,传入接口的全类名,返回java.util.ServiceLoader,ServiceLoader是一个Iterable的实现类,可以通过迭代器获取到所有的扩展,进行执行。

具体不清楚的可以参考下我的另一篇专门介绍SPI的文章:

Java SPI内容详解 学习交流 463257262

二、Dubbo扩展详解

1.Dubbo中的扩展点的增强

  在Dubbo中的扩展点主要是对JDK的扩展点思想做了增强,主要增强了一下功能:

  • 全类名文件中的内容通过key-value的规范书写,加载时也是K-V的存储方式,增加扩展点查找的灵活性
  • JDK中的扩展点的加载会一次性的将所有的扩展点加载到内存中,如果有些扩展点没用,但是改扩展点初始化很耗时,JDK也会将所有的扩展点加载到内存中,这些会造成一些浪费,而Dubbo中的扩展点会按需进行加载(加载时传入扩展点的name,这也是需要依赖于文件的K-V格式)
  • Dubbo增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。同时在对扩展点进行依赖注入时,也会通过扫描到的Wrapper对扩展点实现进行包装。

2.Dubbo中扩展点的使用方式

  • 定义一个接口,在接口上标注一个@SPI,标识这是一个扩展点
  • 在扩展点实现工程中创建文件:/META-INF/dubbo/扩展点全类名
  • 在文件中定义扩展点实现的k-v格式数据
helloService=com.bobo.spring.cloud.alibaba.consumer.spi.impl.HelloServiceImpl
  • 调用如下代码获取扩展的实现进行调用
HelloService HelloService = ExtensionLoader
.getExtensionLoader(HelloService.class)
.getExtension("helloService");
System.out.println(HelloService.sayHello("wangxing"));

3.Dubbo扩展点源码分析

  在Dubbo中存在了以下三种类型的扩展点(Q群:463257262):

  1. 指定名称扩展点
  2. 自适应扩展点
  3. 激活扩展点

3.1 自适应扩展点源码分析

  在Dubbo中,通过在接口上标注【@SPI】标识该接口是一个扩展点,同时在其扩展点实现类或方法上,如果存在【@Adaptive】注解,则表示该类或方法是一个自适应的扩展点。标注在类上时,表示该扩展类是默认的自适应扩展点,标注在方法上时,表示该方法是自适应扩展点,将会重写该方法,使得Dubbo能够在运行时获取到具体的扩展点。加下来就进入源码的分析吧…

  Dubbo的扩展点的入口如下:

HelloService helloService2 = ExtensionLoader
.getExtensionLoader(HelloService.class)
.getAdaptiveExtension();

  首先进入到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 an interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
} 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;
}

  该方法主要做了以下几件事

  • 对传入的的扩展点进行判断

    空值判断

    是否是接口判断

    是否标识了@SPI注解的判断,这也印证了前面我们说的只有标识了@SPI注解的接口Dubbo才会认为它是个扩展点接口

  • 根据类型从EXTENSION_LOADERS缓存中获取ExtensionLoader,获取到就直接返回ExtensionLoader。(EXTENSION_LOADERS是一个CurrentHashMap集合,key为扩展点接口的.class对象,value为该扩展点对应的ExtensionLoader)

  • 如果缓存中未获取到的ExtensionLoader,以扩展点.class对象为key,创建一个ExtensionLoader对象为value存储到EXTENSION_LOADERS中,返回创建的ExtensionLoader。到此就可以获取到一个ExtensionLoader了,通过返回的ExtensionLoader对象可以获得对应的扩展点的实现对象

接下来进入ExtensionLoader类中的构造方法,看看ExtensionLoader实例化时做了什么

private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

  在构造器中为type属性复制传入的扩展点的.class对象。同时通过自适应扩展点的方式获取到了一个ExtensionFactory的扩展点的实现,赋值给objectFactory。这里先不详细说明会具体获取到哪个实现,本节分析完成再回过来看。

  通过以上的步骤,一个初始化完成的ExtensionLoader对象已经被获取到了,分析getAdaptiveExtension()获取自适应扩展点实现的流程

  进入getAdaptiveExtension()

public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
// 如果获取到的实例为null,切缓存的错误不能null,抛出异常
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
} synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
// 异常信息缓存起来,下一次进来时如果发现是创建实例是出现异常,就直接抛出异常。这里的设计应该是当扩展点创建异常时避免多次执行创建流程的优化
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
return (T) instance;
}

该方法主要做了以下几件事

  • 从缓存cachedAdaptiveInstance中获取扩展点实现,存在该扩展点对象,则直接返回该实例。cachedAdaptiveInstance是一个Holder对象,主要用于缓存该扩展点的实现的具体实例,因为这里只会返回一个自适应扩展点的实现(有多个实现类标注了则按文件定义顺序取最后一个),实现对于每个``ExtensionLoader`来说,自适应扩展点是单例的。

  • 如果扩展点实现不存在,调用createAdaptiveExtension()创建一个具体的实现,并将该实例set到cachedAdaptiveInstance中缓存起来。

创建扩展点实现的具体流程是在createAdaptiveExtension方法中

 private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

  该方法主要做了以下几件事

  • 调用getExtensionClasses(),顾名思义,该方法主要是获取扩展点的所有实现的.class对象。
  • 如果缓存的cachedAdaptiveClass 对象不为null,直接返回。(cachedAdaptiveClass是一个class对象,用于保存该扩展点的自适应扩展点的实现,即是该扩展点的实现类中存在有将@Adaptive标注在类上的默认自适应扩展点)
  • 如果为缓存有cachedAdaptiveClass对象,则调用createAdaptiveExtensionClass创建一个cachedAdaptiveClass,并复制给cachedAdaptiveClass

接下来首先进入getExtensionClasses()方法

private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}

  该方法首先会从cachedClasses(cachedClasses也是一个holder,用于存储每个扩展点的所有扩展实现的map集合)获取该.class对象,存在则直接返回,否则调用loadExtensionClasses方法加载扩展点的classs,将加载到的classes存到cachedClasses中。

接下来进入loadExtensionClasses

private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName(); Map<String, Class<?>> extensionClasses = new HashMap<>(); for (LoadingStrategy strategy : strategies) {
// 扫描每个加载策略目录中的扩展点实现
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
// 对alibaba的践行兼容
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
} return extensionClasses;
}

  首先调用cacheDefaultExtensionName()方法。再给接口标注@SPI的时候,可以给个默认的value值,表示指定的默认的扩展点实现,如@SPI("dubbo")public interface Protocol 表示默认的扩展点为扩展点实现名为dubbo的实现。而cacheDefaultExtensionName方法就是通过注解获取到该扩展点的默认扩展点name,赋值给cachedDefaultName

  遍历strategies,获取到多个LoadingStrategy,通过stratery.directory()获取到需要扫描的目录,以下是Dubbo中默认的三种策略的实现

  • DubboInternalLoadingStrategy --> META-INF/dubbo/internal/
  • DubboLoadingStrategy -->META-INF/dubbo/
  • ServicesLoadingStrategy --> META-INF/services/

  在Dubbo中,创建ExtensionLoader对象时,会load到所有的LoadingStrategy,这里利用的是JDK原生的SPI的方式,将LoadingStrategy的所有扩展实现都加载进来,保存到strategies中。所以如果需要扩展Dubbo中的扫描的路径,按照JDK的原生方式进行扩展即可

  进入到loadDirectory方法

private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls = null;
ClassLoader classLoader = findClassLoader(); // try to load from ExtensionLoader's ClassLoader first
if (extensionLoaderClassLoaderFirst) {
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
urls = extensionLoaderClassLoader.getResources(fileName);
}
} if (urls == null || !urls.hasMoreElements()) {
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
} if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}

  该方法首先扫描传入路径下的所有的以type全类名命名的文件,获取到资源,将获取到的文件转换成Resource,传入到loadResource()方法中

private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
try {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
String line;
// 按行读取文件中的每行数据
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;
// 通过等号分割,等号前的为key(扩展点name),等号后的为类的全类名
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0 && !isExcluded(line, excludedPackages)) {
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name, overridden);
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}

  首先按行读取文件,对读取到的每一行数据通过=号进行分割,=号前为扩展点的名字,=号后的为扩展点的具体扩展的实现类的全类名

  通过Class.forName将实现类加载到内存中。传入到loadClass()方法

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
boolean overridden) 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.");
}
if (clazz.isAnnotationPresent(Adaptive.class)) {
// 会覆盖掉之前保存的`cachedAdaptiveClass`
cacheAdaptiveClass(clazz, overridden);
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
} else {
clazz.getConstructor();
if (StringUtils.isEmpty(name)) {
// 如果扩展文件中的name为空,则调用findAnnotationName方法获取扩展点名字,具体命名方式这里就不详细看了
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, n, overridden);
}
}
}
}

  该方法主要主要做了以下几件事

  • 判断该对象是否实现了扩展点接口,未实现则抛出异常
  • 判断给实例是否是自适应扩展点,是,调用cacheAdaptiveClass方法将该扩展点复制到cachedAdaptiveClass成员变量中。
  • 判断该实例是否是扩展点的wrapper,是则调用cachedWrapperClasses方法将该实例保存到cachedWrapperClasses中。扩展点实现是否是wrapper的判断条件为该实现类中存在一个以扩展点为入参的构造方法时。
  • 对name进行分割,获取到单个扩展点名字,检查是否是扩展点,是,则将该实例存储到cachedActivates中
  • 缓存扩展点的名字,存储到cachedNames中,以扩展点具体实现类的.class为key,扩展点name为value
  • 调用saveInExtensionClass方法,将扩展点名字及其实现的.class保存到extensionClasses()集合中。

  到这里,该扩展点在项目中的所有实现将被加载完成,且已经区分出了实现中,自适应扩展点,wrapper等不同类型的实现。然后我们回到

  再次回到getAdaptiveExtensionClass()方法,当执行完getExtensionClasses();方法之后,如果cacheAdaptiveClass为null,表示该扩展点没有默认的自适应扩展点,此时扩展点需要将需要自适应扩展的方法上标注@Adaptive(),并且该方法中需要传入URL对象,因为Dubbo中需要将都是通过URL来携带配置的。

  将调用createAdaptiveExtensionClass()方法动态创建一个自适应扩展点

private Class<?> createAdaptiveExtensionClass() {
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}

  该方法会动态生成一个自适应扩展点的类,然后编译通过编译器编译,加载其.class文件到内存中。返回该动态类的.class对象。

  生成的动态类代码如下:

import org.apache.dubbo.common.extension.ExtensionLoader;

public class HelloService$Adaptive implements com.wangx.spring.cloud.alibaba.consumer.spi.HelloService {
public java.lang.String sayHello(org.apache.dubbo.common.URL arg0, java.lang.String arg1) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
// 默认类名分割。可以在@Adaptive注解中指定该参数的名称,default为SPI上定义的默认扩展点实现
String extName = url.getParameter("hello.service","default");
if (extName == null)
throw new IllegalStateException("Failed to get extension (com.wangx.spring.cloud.alibaba.consumer.spi.HelloService) name from url (" + url.toString() + ") use keys([hello.service])");
// 根据名称获取到该扩展点类型的扩展实现
com.wangx.spring.cloud.alibaba.consumer.spi.HelloService extension = (com.wangx.spring.cloud.alibaba.consumer.spi.HelloService) ExtensionLoader.getExtensionLoader(com.wangx.spring.cloud.alibaba.consumer.spi.HelloService.class).getExtension(extName);
return extension.sayHello(arg0, arg1);
}
}

  该类实现了 扩展点接口,重写了扩展点中的自适应扩展方法。该方法体现的是,在运行时通过传入的URL的信息动态的获取处理当前URL时的扩展点的实现。

  到这里就可以返回各种情况下的自适应扩展点的.class对象了,接下来再次回到createAdaptiveExtension方法中,通过以上的一系列操作,我们已经获取到了自适应扩展点的.class对象,并调用反射创建一个扩展点实现的对象。然后调用injectExtension进行依赖注入

private T injectExtension(T instance) {

        if (objectFactory == null) {
return instance;
} try {
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
} try {
String property = getSetterProperty(method);
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
} }
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}

  在injectExtension方法中,有如下几个操作:

  • 判断是否存在objectFactory,为null,则直接返回实例对象
  • 遍历该对象的所有方法,过滤掉不是setter方法及被标注了@DisableInject注解的方法,过滤表setter方法参数类型为特定类型及原生类型的方法,setter方法的参数就是需要被注入的对象。
  • 根据setter方法获取被依赖注入的属性名称,然后通过 objectFactory.getExtension(pt, property);获取到被注入对象实例,执行setter方法进行依赖注入。

  在初始化ExtensionLoader对象时,objectFactory是通过如下代码获取的

objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());

  通过上面的一些列分析,现在已经可以知道这是一个ExtensionFactory的自适应的扩展点,根据我们自适应扩展点的获取方式,我们可以推断出该扩展点的自适应扩展点的实现。

  在ExtensionFactory的所有子类实现中,我们找到了AdaptiveExtensionFactory类,该类上标注了@Adaptive,所以可以推断出objectFacotory的指向的就是AdaptiveExtensionFactory类的对象。

  下面来看看AdaptiveExtensionFactory类中的实现:

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory { private final List<ExtensionFactory> factories; public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
} @Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
} }

  在AdaptiveExtensionFactory的构造函数中,会先获取到ExtensionFactory类型的ExtensionLoader,然后通过调用loader.getSupportedExtensions()获取到ExtensionFactory的所有扩展实现的名字。通过loader.getExtension(name)根据名称获取到所有扩展点,存储到factories中。

  在injectExtension中调用getExtension()方法时,将会遍历初始化时获取到的所有的ExtensionFactory的扩展点。只要在其中的一个扩展到那点中找到该扩展对象的实例,则直接返回。传入的对象type和name,获取Dubbo环境下可能存在的扩展点。

  在injectExtension方法中返回的依赖注入完成的对象,即是我们需要获取的自适应扩展点对象

3.2 根据名称获取扩展点

  根据名称获取扩展点,顾名思义,就是根据扩展点的名称,获取到扩展点对应的实现。这种方式在Dubbo中也被广泛应用到,主要是可以通过URL中的参数或协议作为name,在运行时根据URl动态的获取到不同方式的实现。比如获取负载均衡器等

入口如下:

HelloService HelloService = ExtensionLoader.getExtensionLoader(HelloService.class).getExtension("helloService");

getExtensionLoader方法在上述中已经解释清楚了,现在直接进入到getExtension方法中

public T getExtension(String name) {
//判断传入名称是否为null
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
// 如果为true,获取默认的扩展点,在getDefaultExtension方法中会调用getExtensionClasses->loadExtensionClasses方法,该方法中的cacheDefaultExtensionName会将默认扩展点的name赋值到cachedDefaultName中,所以当调用getDefaultExtension()即可获得默认的扩展点实现
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) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}

  该方法主要做了以下几个操作

  • 如果传入的name为true,则获取该扩展点的默认扩展点
  • 获取或新建一个Holder,这里跟自适应扩展点不同的是,自适应扩展点的只有一个实现会被保存,而通过名称获取扩展点时,需要将每个name对应的扩展点实现包装的holder放存储到cachedInstances中,cachedInstances是一个map集合,保存了name对应的扩展的实现。如果不存在该holder,则新建一个holder对象返回,获取holder保存的instance对象,如果不存在,则直接利用双重检查锁创建一个单例的instance保存到holder中。

  创建instance时,需要调用createExtension(name)方法

@SuppressWarnings("unchecked")
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, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}

  通过自适应扩展点的分析,我们已经知道了getExtensionClasses()方法返回的是扩展点name为key,value为具体扩展点实现类的class对象。然后可以通过name获取到扩展点对应的class对象。然后通过该class对象,到EXTENSION_INSTANCES缓存中获取实现类的对象,如果不存在,则直接通过反射创建一个对象。

  接下来就跟自适应扩展点一样,调用injectExtension进行依赖注入。依赖注入完成之后,将对该对象进行包装,首先从加载时装载的cachedWrapperClasses缓存中获取所有的wrapper扩展点,遍历所有的装饰器,将创建的实际的扩展点对象通过构造器传入到wrapper中,反射创建出一个wrapper对象,在对该wrapper对象进行依赖注入,完成之后将该对象复制给instance。

  比如现在有一个扩展点S,扩展点实现F,该扩展点有A,B,C三个包装器,那么通过如上遍历包装器之后,最后的到的instance对象的结构可能经过了层层的嵌套,变成了这个样子:A(B(C(F)))。调用instance时,将会从最外层开始执行该对象的方法,最终到最里层才会执行实际扩展点的方法。这种设计使得包装的实现更加简洁和灵活。

  然后调用initExtension方法,如果该对象实现了Lifecycle接口,则调用initialize方法。最后返回一个被包装的对象

3.3 激活扩展点

  激活扩展点的使用也需要在实现类上标注@Activate注解。注解中可以指定group和value,当不指定时,就无条件激活,否则就按照指定的条件进行激活,激活扩展点的入口为:

List<HelloServiceActive> li  = ExtensionLoader.getExtensionLoader(HelloServiceActive.class).getActivateExtension(url,"helloService2");
for (HelloServiceActive helloServiceActive : li) {
helloServiceActive.say();
}

  需要传入一个URL对象和一个需要获取激活的的扩展点的参数看key,比如在参数中设置url = url.addParameter("xing","xing");传入的key为xing。进入getActivateExtension()方法,因为可以指定group等,所以最终调用的方法为

public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : asList(values);
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
getExtensionClasses();
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Object activate = entry.getValue(); String[] activateGroup, activateValue; if (activate instanceof Activate) {
activateGroup = ((Activate) activate).group();
activateValue = ((Activate) activate).value();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
} else {
continue;
}
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)) {
activateExtensions.add(getExtension(name));
}
}
activateExtensions.sort(ActivateComparator.COMPARATOR);
}
List<T> loadedExtensions = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (DEFAULT_KEY.equals(name)) {
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(0, loadedExtensions);
loadedExtensions.clear();
}
} else {
loadedExtensions.add(getExtension(name));
}
}
}
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(loadedExtensions);
}
return activateExtensions;
}

  经过我们对上面两种方式的分析,其实第三种方式我们已经能够很轻松的看懂了。主要流程有,先遍历扫描是装载在cachedActivates中的所有激活扩展点,跟url和group进行匹配,匹配成功,则通过扩展点的名name通过根据名称获取扩展点的方式获取扩展点,存储到list中。然后遍历根据key获取到的value解析出来的扩展点名称,通过该名称获取到扩展点装载到list中,然后返回activateExtensions();

  这样就完成了通过激活扩展点的获取。

Dubbo系列讲解之扩展点实现原理分析【2万字分享】的更多相关文章

  1. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  2. Dubbo系列讲解之服务注册【3万字长文分享】 23/100 发布文章

    服务注册的几个步骤   对于RPC框架的服务注册,一般包含了如下的流程: 加载服务提供者,可能是通过xml配置的,也可能是通过扫描注解的 实例化服务提供者,并以服务接口作为key,实现类作为value ...

  3. dubbo 序列化机制之 hessian2序列化实现原理分析

    对于远程通信,往往都会涉及到数据持久化传输问题.往大了说,就是,从A发出的信息,怎样能被B接收到相同信息内容!小点说就是,编码与解码问题! 而在dubbo或者说是java的远程通信中,编解码则往往伴随 ...

  4. JVM源码系列:ThreadMXBean 打出堆栈信息原理分析

    我们通常会使用工具jstack 去跟踪线程信息,其如何实现使用attach 的方式还是ptrace 的方式,这些可以去参考本人的博客的其他文章. 但这些方式都是外部使用的方式,如何直接使用java代码 ...

  5. Dubbo——SPI及自适应扩展原理

    文章目录 引言 正文 一.什么是SPI? 1. Java SPI的实现 2. Dubbo SPI实现原理 由配置文件得到的猜想 SPI源码 二.自适应扩展机制 三.Dubbo IOC 总结 引言 Du ...

  6. Dubbo系列之 (一)SPI扩展

    一.基础铺垫 1.@SPI .@Activate. @Adaptive a.对于 @SPI,Dubbo默认的特性扩展接口,都必须打上这个@SPI,标识这是个Dubbo扩展点.如果自己需要新增dubbo ...

  7. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  8. dubbo系列四、dubbo服务暴露过程源码解析

    一.代码准备 1.示例代码 参考dubbo系列二.dubbo+zookeeper+dubboadmin分布式服务框架搭建(windows平台) 2.简单了解下spring自定义标签 https://w ...

  9. 原理分析dubbo分布式应用中使用zipkin做链路追踪

    zipkin是什么 Zipkin是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper的论文设计而来,由 Twitter 公司开 ...

随机推荐

  1. 『无为则无心』Python函数 — 26、Python函数参数的传递方式

    目录 1.位置参数 2.关键字参数 3.缺省参数(默认参数) 4.不定长参数(可变参数) (1)包裹位置传递 (2)包裹关键字传递 5.位置参数.默认参数.可变参数的混合使用 6.拓展:参数解包 提示 ...

  2. 在docker中使用nginx部署前端项目

    前言 部署了三个nginx用于前端项目, 并使用keepalived部署好热备, 所以总共有5个nginx 创建好nginx的文件和配置 根据上面的指令创建好目录 mkdir /home/web/ng ...

  3. Hystrix 使用说明

    1.什么情况下会触发 fallback 方法 名字 描述 触发fallback EMIT 值传递 NO SUCCESS 执行完成,没有错误 NO FAILURE 执行抛出异常 YES TIMEOUT ...

  4. linux菜鸡学习之路

    Linux入门 Linux 介绍 1.Linux怎么读 2.Linux是一款操作系统,免费,开源,安全,高效,稳定,处理高并发非常强悍. Linux文件系统目录 基本介绍 linux的文件系统树状目录 ...

  5. QT从入门到入土(一)——Qt5.14.2安装教程和VS2019环境配置

    引言 24岁的某天,承载着周围人的关心,一路南下.天晴心静,听着斑马,不免对未来有些彷徨.但是呢,人生总要走陌生的路,看陌生的风景,所幸可以听着不变的歌,关心自己的人就那么多.就像是对庸常生活的一次越 ...

  6. 【LeetCode】203.移除链表元素

    203.移除链表元素 知识点:链表:双指针 题目描述 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 . 示例 ...

  7. glibc库和glib库

    http://ftp.acc.umu.se/pub/GNOME/sources/ ubuntu16上glib的安装包的名是libglibXX  XX是版本号 ubuntu上查看glibc的方法 ldd ...

  8. 【Mysql】InnoDB 引擎中的页目录

    一.页目录和槽 接上一篇,现在知道记录在页中按照主键大小顺序串成了单链表. 那么我使用主键查询的时候,最顺其自然的办法肯定是从第一条记录,也就是 Infrimum 记录开始,一直向后找,只要存在总会找 ...

  9. PAT甲级:1089 Insert or Merge (25分)

    PAT甲级:1089 Insert or Merge (25分) 题干 According to Wikipedia: Insertion sort iterates, consuming one i ...

  10. 一定要收藏的5个优秀的SpringCloud开源项目

    上一期为大家推荐了几个前端模板,没看过的点下面 一定要收藏的5个后台管理系统的前端框架 今天再为大家推荐几个优秀的SpringCloud开源脚手架项目,开箱即用,不管是学习还是开发新项目,都非常不错. ...