一、什么是SPI

  SPI全称为Service Provider Interface,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件。这样可以在运行时,动态为该接口替换实现类。
  JDK提供了默认的SPI实现,但是Dubbo并未使用JDK提供的SPI,而是自己封装了一套。我们先来通过Dubbo官网给的两个例子简单了解下JDK和Dubbo的SPI是如何使用的。

1.1.JDK SPI示例

  首先定义一个接口以及它的两个实现类
 public interface Robot {
void sayHello();
} public class OptimusPrime implements Robot { @Override
public void sayHello() {
System.out.println("Hello, I am Optimus Prime.");
}
} public class Bumblebee implements Robot { @Override
public void sayHello() {
System.out.println("Hello, I am Bumblebee.");
}
}

  接下来,在项目(以一个典型的maven项目为例)的“resources/META-INF/services”路径下创建一个文件,名称为Robot的全限定名“org.apache.spi.Robot”。文件内容如下:

org.apache.spi.OptimusPrime
org.apache.spi.Bumblebee

  编写测试代码,运行之后可以看到两个实现类被加载并调用了sayHello方法(调用结果演示略)。

public class JavaSPITest {

    @Test
public void sayHello() throws Exception {
ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
System.out.println("Java SPI");
serviceLoader.forEach(Robot::sayHello);
}
}

1.2.Dubbo SPI示例

  仍然使用JDK SPI示例中的接口和其实现类代码,不过需要在接口Robot上添加注解@SPI
//使用SPI注解标注的接口
@SPI
public interface Robot {
void sayHello();
}

  接下来,在项目(以一个典型的maven项目为例)的“resources/META-INF/dubbo”路径下创建一个文件,名称为Robot的全限定名“org.apache.spi.Robot”。文件内容如下:

optimusPrime=org.apache.spi.OptimusPrime
bumblebee=org.apache.spi.Bumblebee
  通过测试类进行测试,则会执行相应实现类的sayHello方法:
//测试类
public class DubboSPITest { @Test
public void sayHello() throws Exception {
//传入一个标注有@SPI的接口Class,通过getExtensionLoader获取该SPI接口的ExtensionLoader实例
ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
//传入要获取的实现类的name(META-INF/dubbo/org.apache.spi.Robot文件下的name),获取实现类实例
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
optimusPrime.sayHello();
Robot bumblebee = extensionLoader.getExtension("bumblebee");
bumblebee.sayHello();
}
}

  通过与JDK SPI示例的比较,发现Dubbo SPI与JDK SPI最大的不同就是Dubbo SPI通过键值对的方式进行配置。这样最大的好处是可以按需加载指定的实现类(通过name指定)。下面就让我们以本例中getExtensionLoader与getExtension两个方法作为引子,分析Dubbo SPI的实现源码。

 

二、getExtensionLoader

  该方法根据SPI接口类型创建一个ExtensionLoader实例,即每种类型的SPI接口都有一个相应的ExtensionLoader。代码如下所示:
/**************************************** 相关字段 ****************************************/

//ExtensionLoader对应的SPI接口类型
private final Class<?> type; //ExtensionLoader对应的ExtensionFactory实例
private final ExtensionFactory objectFactory; //ExtensionLoader全局缓存,key为SPI接口类型,value为相应的ExtensionLoader实例
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(); /**************************************** 相关方法 ****************************************/ /**
* 静态方法,根据SPI接口类型获取相应的ExtensionLoader
*/
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//判断type是否为空、是否是接口类型、是否具有@SPI注解
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...");
} //从ExtensionLoader的缓存中根据SPI接口类型获取对应的ExtensionLoader实例
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
//若缓存没有该实例,则new一个,并且存放入缓存,key为type
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
} /**
* 私有构造器,对调用者而言,只能通过getExtensionLoader方法获取ExtensionLoader实例
*/
private ExtensionLoader(Class<?> type) {
//保存该ExtensionLoader的SPI接口类型信息
this.type = type;
//若SPI接口类型为ExtensionFactory,则不设置字段ExtensionFactory,
//否则需要设置一个ExtensionFactory。具体的获取逻辑我们后面会讲到
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

三、getExtension

  该方法用于获取ExtensionLoader对应的SPI接口的实现类实例,name用于指定需要获取的实现类类型。在后面,我们将SPI接口实现类统一称为扩展类。

/**************************************** 实例缓存相关字段 ****************************************/

//全部SPI接口的扩展类实例缓存,key为扩展类Class(每一个SPI接口的每一个实现类的Class都不同)
//value为对应的扩展类实例。注意该缓存要与另外一个类似的缓存cachedInstances区分开。
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(); //每个ExtensionLoader对应的SPI接口的扩展类实例缓存,
//key为扩展类的name,value为Holder对象,其持有/维护扩展类的实例。
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>(); /**************************************** Class缓存相关字段 ****************************************/ //每个ExtensionLoader对应的SPI接口的扩展类Class缓存,key为扩展类的name,value为扩展类Class
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>(); //SPI接口的扩展类中具有@Adaptive注解的扩展类Class缓存
private volatile Class<?> cachedAdaptiveClass = null; //SPI接口的扩展类中被判定为具有包装功能的扩展类Class缓存
private Set<Class<?>> cachedWrapperClasses; //SPI接口的扩展类中具有@Activate注解的缓存,key是扩展类names[0],value为@Activate的Class
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>(); /**************************************** name缓存相关字段 ****************************************/ //SPI接口的扩展类的name缓存,key为扩展类的Class,value为扩展类的names[0]
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>(); //SPI接口的默认扩展类的name
private String cachedDefaultName; /**************************************** 其他相关字段 ****************************************/ //ExtensionLoader对应的SPI接口Class
private final Class<?> type; /**************************************** 相关方法 ****************************************/ /**
* 获取ExtensionLoader对应的SPI接口的扩展类实例
*/
public T getExtension(String name) {
//检查扩展类的name
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
//如果传入的name值为true,则获取默认的SPI接口扩展类实例
if ("true".equals(name)) {
return getDefaultExtension();
}
//getOrCreateHolder方法比较简单,它从缓存“cachedInstances”中获取该name对应的Holder实例,
//Holder是一个“持有类”,其可能持有扩展类实例
Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
//如果未获取到实例,在监视器锁中进行第二次获取与创建
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//创建name对应的扩展类实例,并缓存到cachedInstances中
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
} /**
* 获取默认的扩展类实例
*/
public T getDefaultExtension() {
//调用getExtensionClasses方法获取SPI接口配置的扩展类信息,返回结果为一个Map<String, Class>,
//其中key为扩展类name,value为该name对应的扩展类Class。
getExtensionClasses(); //如果SPI接口默认扩展类name为空或者为true,则默认的扩展类实例为null
if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
return null;
}
//否则调用getExtension根据默认的扩展类name去获取实例
return getExtension(cachedDefaultName);
} /**
* 创建一个扩展类的实例
*/
private T createExtension(String name) {
//调用getExtensionClasses方法,从返回结果中获取name对应的扩展类Class,如果Class为null,则抛出异常
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
//从缓存“EXTENSION_INSTANCES”中根据扩展类Class获取实例
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
//缓存未命中则通过反射创建一个实例,并存入缓存
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//向这个扩展类实例注入其所需要的属性(以setXXX为准),该方法比较复杂,我们后面会进行分析
injectExtension(instance);
//获取SPI接口扩展类中具有包装功能的扩展类Class缓存,然后对instance进行层层包装(装饰器模式),
//对每次包装出来的新实例进行属性注入,全部包装完成后让instance指向最终的包装结果实例
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance ... couldn't be instantiated: ");
}
} /**
* 获取SPI接口配置的扩展类信息
*/
private Map<String, Class<?>> getExtensionClasses() {
//从缓存“cachedClasses”中获取已加载的扩展类
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//缓存为空,调用loadExtensionClasses方法加载SPI接口配置的扩展类信息,并缓存结果
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
} /**
* 加载SPI接口配置的扩展类信息
*/
private Map<String, Class<?>> loadExtensionClasses() {
//获取SPI接口默认扩展类的name并进行缓存
cacheDefaultExtensionName(); //读取并解析SPI接口的配置文件,去几个固定的目录下读取
//(1):META-INF/services/SPI接口全限定类名(或SPI接口全限定类名替换org.apache为com.alibaba)
//(2):META-INF/dubbo/SPI接口全限定类名(或SPI接口全限定类名替换org.apache为com.alibaba)
//(3):META-INF/dubbo/internal/SPI接口全限定类名(或SPI接口全限定类名替换org.apache为com.alibaba)
Map<String, Class<?>> extensionClasses = new HashMap<>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache",
"com.alibaba"));
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache",
"com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache",
"com.alibaba"));
return extensionClasses;
} /**
* 获取SPI接口默认扩展类的name并进行缓存
*/
private void cacheDefaultExtensionName() {
//获取ExtensionLoader对应的SPI接口上的SPI注解
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
//获取SPI注解的value值并进行校验
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("More than 1 default extension name ...");
}
if (names.length == 1) {
//将其作为SPI接口默认扩展类的name并进行缓存
cachedDefaultName = names[0];
}
}
}
} /**
* 加载SPI接口的配置文件
*/
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls;
//获取并使用类加载器去加载文件(同名文件进行内容合并)
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
//遍历获取到的URL,并调用loadResource去加载资源
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: ...");
}
} /**
* 在loadDirectory的基础上,对每个文件中的内容进行加载与解析
*/
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
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;
//按“=”号截取name和扩展类的全限定类名,可以看出name是可以没有的
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
//如果line,即扩展类全限定类名不为空,通过Class.forName获取其Class,
//然后调用loadClass继续加载该Class
if (line.length() > 0) {
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load ...");
exceptions.put(line, e);
}
}
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: ...");
}
} /**
* 在loadResource的基础上,对文件中的每行获取到的Class进行分析,并进行缓存
*/
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
//判断配置的扩展类是否为指定SPI接口的实现类
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading ... is not subtype of interface.");
}
if (clazz.isAnnotationPresent(Adaptive.class)) {
//若扩展类有@Adaptive注解,将这个Class存入缓存“cachedAdaptiveClass”
//注意:一个SPI接口配置文件中,只能配置一个有@Adaptive注解的扩展类
cacheAdaptiveClass(clazz);
} else if (isWrapperClass(clazz)) {
//若扩展类具有clazz.getConstructor(type)这样的构造器,则认为其是一个具有包装功能的扩展类,
//并将其存入缓存“cachedWrapperClasses”,一个SPI接口可以配置多个用于包装的扩展类
cacheWrapperClass(clazz);
} else {
//进入到这个分支,表示该Class只是一个普通的SPI接口扩展类。
//判断该扩展类是否具有无参构造器
clazz.getConstructor();
//如果该扩展类在SPI接口配置文件中未定义name,则判断扩展类是否具有@Extension注解,
//如果有,则以@Extension的value值作为name;否则以扩展类的SimpleName作为name,
//并且,如果扩展类的SimpleName以SPI接口的SimpleName作为后缀结尾,则name需要去掉该后缀
if (StringUtils.isEmpty(name)) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class ...");
}
}
//对name按照","进行分割
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
//names不为空
//若扩展类具有@Activate注解,则使用names数组的第一个元素作为key,@Activate的Class为value
//将映射关系存入缓存“cachedActivates”
cacheActivateClass(clazz, names[0]);
for (String n : names) {
//将该扩展类的name和Class存入缓存“cachedNames”,name保持为names[0]
cacheName(clazz, n);
//将该扩展类的name和Class存入方法参数extensionClasses
saveInExtensionClass(extensionClasses, clazz, name);
}
}
}
}

  通过以上的源码分析,我们了解到getExtension方法获取一个SPI接口的扩展类实例的流程分为解析配置文件、加载并缓存扩展类、创建并加工扩展类实例几个步骤。

  在得到一个最终可用的扩展类实例前,该实例会进行属性注入与层层包装,这些行为在官网上被成为扩展点特性,这里我们把它称之为“扩展类特性”。官方给出了4个特性,分别为“扩展点自动包装”、“扩展点自动装配”、“扩展点自适应”以及“扩展点自动激活”。在下面的其余章节中,我们将重点来研究这几个扩展点特性。

四、扩展点自适应

  在分析与getExtension方法密切相关的扩展点自动装配与扩展点自动包装之前,我们先来了解一下扩展点自适应这个特性。扩展点自适应是一个非常重要的特性,因为dubbo某些SPI接口的扩展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载,这种动态决定调用哪个扩展类实例的方式使得dubbo非常的灵活。此外,先了解这个特性,也有助于我们更好的分析自动装配与自动包装的源码。  

4.1.什么是自适应扩展类

  自适应扩展类同样是某个SPI接口的扩展类,该扩展类具备这样的一些特征:它没有实际的业务逻辑,而是能够根据传入的参数,动态的获取对应的扩展类实例。可以看一下官网给的例子:
/**
* 一个模拟的SPI接口
*/
@SPI
public interface WheelMaker {
Wheel makeWheel(URL url);
} /**
* SPI接口的自适应扩展类
*/
public class AdaptiveWheelMaker implements WheelMaker {
//自适应扩展类该方法的逻辑为通过URL中的参数,动态的获取真正需要执行的SPI接口扩展类
public Wheel makeWheel(URL url) {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
// 1.从URL中获取WheelMaker名称
String wheelMakerName = url.getParameter("Wheel.maker");
if (wheelMakerName == null) {
throw new IllegalArgumentException("wheelMakerName == null");
}
// 2.通过SPI加载具体的WheelMaker
WheelMaker wheelMaker = ExtensionLoader.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName);
// 3.调用目标方法
return wheelMaker.makeWheel(URL url);
}
} /**
* 一个模拟的SPI接口
*/
@SPI
public interface CarMaker {
Car makeCar(URL url);
} /**
* 汽车制造者SPI接口的扩展类
*/
public class RaceCarMaker implements CarMaker { WheelMaker wheelMaker; //在injectExtension方法中会通过setter注入AdaptiveWheelMaker
//目前我们只知道自动装配行为的结果,原因会在“扩展点自动装配”小节分析
public setWheelMaker(WheelMaker wheelMaker) {
this.wheelMaker = wheelMaker;
} //实现的方法
public Car makeCar(URL url) {
//实际调用AdaptiveWheelMaker的makeWheel方法,获得当前运行环境参数url下需要使用的Wheel扩展类
Wheel wheel = wheelMaker.makeWheel(url);
return new RaceCar(wheel, ...);
}
}

  假设运行时传入这样一个url参数“dubbo://192.168.0.101:20880/XxxService?wheel.maker=MichelinWheelMaker”,那么RaceCar最终的wheel为扩展类“MichelinWheelMaker”制造出来的轮胎。

4.2.获取自适应扩展类实例

  让我们回到ExtensionLoader类,在该类中提供了getAdaptiveExtension方法用于获取一个SPI接口的自适应扩展类的实例,这个方法的源码分析如下:
/**************************************** 相关字段 ****************************************/

//缓存的自适应扩展类实例
private final Holder<Object> cachedAdaptiveInstance = new Holder<>(); //SPI接口的扩展类中具有@Adaptive注解的扩展类Class缓存
private volatile Class<?> cachedAdaptiveClass = null; //创建自适应扩展类实例的异常信息
private volatile Throwable createAdaptiveInstanceError; /**************************************** 相关方法 ****************************************/ /**
* 获取自适应扩展类的实例
*/
public T getAdaptiveExtension() {
//从ExtensionLoader的缓存字段中获取数据,cachedAdaptiveInstance为一个Holder<Object>对象
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
//需要判断一下创建自适应扩展对象的Throwable缓存是否存在,如果存在,直接抛出
if (createAdaptiveInstanceError == null) {
//并发控制
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//创建自适应扩展类实例
instance = createAdaptiveExtension();
//设置缓存
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("......");
}
}
}
} else {
throw new IllegalStateException("......");
}
} return (T) instance;
} /**
* 创建自适应扩展类实例
*/
private T createAdaptiveExtension() {
try {
//(1)调用getAdaptiveExtensionClass方法获取自适应扩展类的Class;
//(2)通过newInstance实例化一个自适应扩展类的对象;
//(3)调用injectExtension方法向自适应拓展类的实例中注入依赖,参考“扩展点自动装配”小节;
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("......");
}
} /**
* 获取自适应扩展类的Class
*/
private Class<?> getAdaptiveExtensionClass() {
//获取该ExtensionLoader对应SPI接口配置的所有扩展类(参考“getExtensionClsses”小节)
getExtensionClasses();
//检查具有@Adaptive注解的扩展类缓存,若缓存不为空,则直接返回缓存
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//如果SPI接口配置的所有扩展类都没有被@Adaptive标注,则创建自适应扩展类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
} /**
* 创建自适应扩展类
*/
private Class<?> createAdaptiveExtensionClass() {
//通过AdaptiveClassCodeGenerator的generate方法创建自适应扩展代码
//该方法会检测SPI接口中是否有被@Adapative注解的方法,对于要生成自适应扩展类的SPI接口
//必须至少包含一个被@Adaptive注解的方法,否则会抛出异常
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
//获取编译器实现类,一样是通过AdaptiveExtension进行获取,获取之后进行编译
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}

  通过getAdaptiveExtension方法的流程可以发现,要想获得一个SPI接口的自适应扩展类实例,有2种方式:

  • 在SPI接口的配置文件中配置具有@Adaptive注解的扩展类,在执行解析SPI接口配置文件方法getExtensionClasses时,它会调用loadClass方法,该方法判断扩展类是否具有@Adaptive注解,如果有,则将该类Class缓存到ExtensionLoader的字段“cachedAdaptiveClass”中,然后直接实例化该Class的实例并进行自动装配;
  • 如果未配置@Adaptive修饰的扩展类,则Dubbo会使用字节码技术创建一个自适应扩展类,前提是SPI接口上至少有一个被@Adaptive注解的方法;
  在上述两种创建自适应扩展类实例的方法中,都与@Adaptive这个注解息息相关,该注解的代码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
String[] value() default {};
}

  可以看到,@Adaptive注解既可以使用在类上,又可以使用在方法上。其包含一个字符串数组的属性,在通过字节码技术创建自适应扩展类时,该属性参与到生成逻辑中,具体的创建逻辑我们马上通过下一节来了解。

4.3.关于创建自适应扩展类

  Dubbo通过字节码技术创建一个自适应扩展类,第一步使用AdaptiveClassCodeGenerator类创建一个自适应扩展类的代码字符串,第二步通过ExtensionLoader获取字节码编译器Compiler并编译加载第一步中的代码字符串,拿到自适应扩展类的Class。下面我们对每一个步骤进行详细的源码分析。

4.3.1.代码拼接

  回顾一下ExtensionLoader.createAdaptiveExtensionClass方法中调用逻辑
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();

  首先传入SPI接口的Class与默认的扩展类名称(即@SPI注解的value值)创建一个AdaptiveClassCodeGenerator实例,之后调用generate方法生成代码,AdaptiveClassCodeGenerator相对应的源码如下:

/**************************************** 相关字段 ****************************************/

//SPI接口类型
private final Class<?> type; //SPI接口默认的扩展类名称,即@SPI注解的value属性值
private String defaultExtName; /**************************************** 相关方法 ****************************************/ /**
* 可以看到,构造器并未做太多的事情,只是简单的将传入的参数为成员变量赋值
*/
public AdaptiveClassCodeGenerator(Class<?> type, String defaultExtName) {
this.type = type;
this.defaultExtName = defaultExtName;
} /**
* 创建自适应扩展类的代码串
*/
public String generate() {
//遍历SPI接口是否具有@Adaptive注解修饰的方法,如果没有则抛出异常
if (!hasAdaptiveMethod()) {
throw new IllegalStateException("......");
}
//按照一个JAVA类的代码组成顺序,拼接代码字符串
StringBuilder code = new StringBuilder();
//拼接包信息字符串: "package" + SPI接口所在的包
code.append(generatePackageInfo());
//拼接import字符串: "import" + ExtensionLoader的全限定名
code.append(generateImports());
//拼接类开头字符串: "public class" + SPI接口简单名 + "$Adaptive implements" + SPI接口全限定名 + "{"
//注意:使用全限定名的原因为import代码串中只导入ExtensionLoader的类,下同
code.append(generateClassDeclaration());
//拼接每一个方法字符串,逻辑比较复杂,在generateMethod方法源码中详细说明
Method[] methods = type.getMethods();
for (Method method : methods) {
code.append(generateMethod(method));
}
//拼接类结束字符串: "}"
code.append("}");
return code.toString();
} /**
* 检测该SPI接口是否具有@Adaptive修饰的方法
*/
private boolean hasAdaptiveMethod() {
return Arrays.stream(type.getMethods()).anyMatch(m -> m.isAnnotationPresent(Adaptive.class));
} /**
* 生成自适应扩展类的方法代码串
*/
private String generateMethod(Method method) {
//获取方法返回值类型全限定名
String methodReturnType = method.getReturnType().getCanonicalName();
//获取方法名
String methodName = method.getName();
//获取方法内容,有无@Adaptive修饰的方法其方法内容不同,详细见下generateMethodContent源码
String methodContent = generateMethodContent(method);
//获取方法参数列表,格式为"参数类型全限定名 arg0, 参数类型全限定名 arg1, ..."
String methodArgs = generateMethodArguments(method);
//获取方法异常抛出,格式为"throws 异常1全限定名, 异常2全限定名, ..."
String methodThrows = generateMethodThrows(method);
//获取一个方法代码串
return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
} /**
* 生成自适应扩展类的方法体代码串
*/
private String generateMethodContent(Method method) {
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
//判断当前要生成的方法是否有@Adaptive注解
if (adaptiveAnnotation == null) {
//对于没有@Adaptive注解的方法,生成"throw new UnsupportedOperationException(...)"代码
return generateUnsupported(method);
} else {
//检查方法参数列表中是否有类型为"com.apache.dubbo.common.URL"的参数
//简单提一下,com.apache.dubbo.common.URL是dubbo框架中各组件的数据总线
int urlTypeIndex = getUrlTypeIndex(method);
if (urlTypeIndex != -1) {
//如果方法参数列表中有URL类型参数,则为该参数生成判断是否为null以及赋值的代码:
//(generateUrlNullCheck方法比较简单,不进行详细的源码分析,这里只给出逻辑)
//if (arg%d == null) throw new IllegalArgumentException("url == null");
//com.apache.dubbo.common.URL url = arg%d;
//d的值为urlTypeIndex
code.append(generateUrlNullCheck(urlTypeIndex));
} else {
//如果方法参数列表中没有URL类型参数,则需要遍历参数列表中每一个参数的类型信息,
//判断哪一个参数具有"public URL getXXX()"签名形式的方法,如果有则停止遍历且生成如下代码:
//(generateUrlAssignmentIndirectly方法比较简单,不进行详细的源码分析,这里只给出逻辑)
//if (arg%d == null) 备注:此处的d为具备"public URL getXXX()"方法的参数下标,从0开始
// throw new IllegalArgumentException("参数全限定名 + argument == null");
//if (arg%d.getter方法名() == null)
// throw new IllegalArgumentException(参数全限定名 + argument getUrl() == null);
//com.apache.dubbo.common.URL url = arg%d.getter方法名();
code.append(generateUrlAssignmentIndirectly(method));
} //获取该方法的@Adaptive注解的属性值value(为一个String数组),这里列出处理逻辑
//如果属性值value为非空数组,直接获取数组内容即可;
//如果value为空数组,则需将SPI接口的类名按照驼峰命名法进行检测,对每个驼峰进行分割并插入"."号,
//然后转为小写,比如LoadBalance经过处理后,得到load.balance
String[] value = getMethodAdaptiveValue(adaptiveAnnotation); //判断当前方法的参数列表中是否有类型为org.apache.dubbo.rpc.Invocation的参数
boolean hasInvocation = hasInvocationArgument(method); //为当前方法参数列表中第一个类型为org.apache.dubbo.rpc.Invocation的参数生成判null语句以及调用语句
//if (arg%d == null) throw new IllegalArgumentException("invocation == null");
//String methodName = arg%d.getMethodName();
//%d是org.apache.dubbo.rpc.Invocation类型的参数在参数列表中的下标
code.append(generateInvocationArgumentNullCheck(method)); //使用前面获取到的字符串数组value以及Invocation参数存在标识拼接获取扩展类extName的代码串,
//这是自适应扩展类核心的代码,因为自适应扩展类的目的就是要根据当前运行参数,
//判断应该获取SPI接口的哪一个扩展类,而ExtensionLoader.getExtension方法的参数就是这个extName
//该方法逻辑比较复杂,判断分支较多,详细的分析在下面generateExtNameAssignment中
code.append(generateExtNameAssignment(value, hasInvocation)); //对上一步获取的局部变量extName拼接其判null代码
//if(extName == null) throw new IllegalStateException("Failed to get extension name from...");
code.append(generateExtNameNullCheck(value)); //生成获取extName对应扩展类实例的代码串,如下:
//%s extension = (%<s)ExtensionLoader.getExtensionLoader(%s.class).getExtension(extName);
//其中%s为成员变量type.getName,即SPI接口的Class的全限定名
code.append(generateExtensionAssignment()); //生成目标方法调用逻辑,被@Adaptive注解的方法,第一个任务是根据运行时参数获取对应的扩展类实例,
//第二个任务就是调用这个实例的同名方法。生成的方法调用代码串格式为:
//(1)如果该方法无返回值"extension.方法名(arg0, arg2, ..., argN);"
//(2)如果该方法有返回值"return extension.方法名(arg0, arg1, ..., argN);"
//其中extension为上一步生成的扩展类实例变量名,arg0、arg1就为当前@Adaptive注解方法的参数名
code.append(generateReturnAndInvocation(method));
} return code.toString();
} /**
* 扩展名extName获取代码串
*/
private String generateExtNameAssignment(String[] value, boolean hasInvocation) {
String getNameCode = null;
//反向遍历value,目的是生成从URL中获取拓展名的代码,生成的代码会赋值给getNameCode变量
for (int i = value.length - 1; i >= 0; --i) {
//当遍历的元素是最后一个元素时
if (i == value.length - 1) {
//若默认扩展名不空(即@SPI注解的value值不空)
if (null != defaultExtName) {
//由于protocol是URL的成员变量,可通过getProtocol方法获取,其他的则是从
//URL的成员变量parameters(一个Map<String, String>)中获取。
//因为获取方式不同,所以这里要判断value[i]是否为protocol
if (!"protocol".equals(value[i])) {
//需要判断一下hasInvocation标识(即当前方法是否有Invocation参数)
if (hasInvocation) {
//生成的代码功能等价于下面的代码:
//url.getMethodParameter(methodName, value[i], defaultExtName)
//注意,methodName是generateInvocationArgumentNullCheck生成的局部变量,下同
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
} else {
//生成的代码功能等价于下面的代码:
//url.getParameter(value[i], defaultExtName)
getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
}
} else {
//生成的代码功能等价于下面代码:
//( url.getProtocol() == null ? defaultExtName : url.getProtocol() )
getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
}
//若默认扩展名为空
} else {
if (!"protocol".equals(value[i])) {
if (hasInvocation) {
//生成的代码格式同上,即
//url.getMethodParameter(methodName, value[i], defaultExtName)
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
} else {
//生成的代码功能等价于:url.getParameter(value[i])
getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
}
} else {
//生成的代码功能等价于:url.getProtocol()
getNameCode = "url.getProtocol()";
}
}
//当遍历的元素不为最后一个时
} else {
if (!"protocol".equals(value[i])) {
if (hasInvocation) {
//生成的代码同上,即:
//url.getMethodParameter(methodName, value[i], defaultExtName)
getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
} else {
//在上一次获取的getNameCode代码结果基础上,再次获取getNameCode,即一层层的获取值
//生成的代码功能等价于下面的代码:
//url.getParameter(value[i], getNameCode)
//以Transporter接口的connect方法为例,最终生成的代码如下:
//url.getParameter("client", url.getParameter("transporter", "netty"))
getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
}
} else {
//在上一次获取的getNameCode代码结果基础上,再次获取getNameCode,即一层层的获取值
//生成的代码功能等价于下面的代码:
//url.getProtocol() == null ? getNameCode : url.getProtocol()
//以Protocol接口的connect方法为例,最终生成的代码如下:
//url.getProtocol() == null ? "dubbo" : url.getProtocol()
getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
}
}
} //返回生成extName的代码:"String extName = getNameCode;"
return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode);
}
   总结一下生成自适应扩展类代码字符串的逻辑,首先Dubbo只会对具有@Adaptive注解的方法才生成详细的代码,而没有@Adaptive注解的代码直接生成一段抛出异常的代码;其次生成的代码逻辑会根据当前方法的参数以及@Adaptive、@SPI注解的value值等因素,获取到扩展类的name,从而通过ExtensionLoader拿到相应的扩展类实例,并调用该实例的同名方法,可以说是做了一个“动态分发”。

4.3.2.代码编译

  完成自适应扩展类的代码串生成后,获取一个字节码编译器对代码进行编译和加载:
 ClassLoader classLoader = findClassLoader();
//获取编译器实现类,一样是通过AdaptiveExtension进行获取,获取之后进行编译
//在这里具体获取Compiler的细节略去,最终获取到JavassistCompiler类的实例进行编译
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);

  对于这段代码,我们可以运用本小节学到知识,来分析一下Compiler接口的自适应扩展类是什么。

  首先看一下Compiler这个SPI接口的配置文件:
adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler
jdk=org.apache.dubbo.common.compiler.support.JdkCompiler
javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler

  一共配置了3个扩展类,根据name的名称,可以肯定AdaptiveCompiler是具有@Adaptive注解的扩展类

@Adaptive
public class AdaptiveCompiler implements Compiler { private static volatile String DEFAULT_COMPILER; public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
} @Override
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
String name = DEFAULT_COMPILER; // copy reference
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {
compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
} }

  根据4.2小节中的代码逻辑,在具有@Adaptive注解修饰的扩展类的前提下,自适应扩展类实例一定获取到这个被修饰的类实例,因此“ExtensionLoader.getExtensionLoader(....Compiler.class).getAdaptiveExtension();”这段代码获取到了一个AdaptiveCompiler的实例。AdaptiveCompiler的compile方法很简单,由于成员变量“DEFAULT_COMPILER”始终为null,其会调用ExtensionLoader的getDefaultExtension方法获取默认的扩展类实例,即@SPI接口注解value值作为name的实例,看看Compiler接口代码:

@SPI("javassist")
public interface Compiler {
Class<?> compile(String code, ClassLoader classLoader);
}

  其默认的扩展类的name为“javassist”,即类“org.apache.dubbo.common.compiler.support.JavassistCompiler”,这个类的具体compile方法不多赘述,就是校验代码字符串格式的正确性,并进行加载。

五、扩展点自动装配

  了解了扩展点自适应后,让我们回到扩展点自动装配这个特性上来。
  在getExtension方法的源码分析中,当扩展类实例通过反射被初始化后,便调用injectExtension方法为其注入属性,官方将获取扩展类实例过程中的这种行为称为“扩展点自动装配”。下面我们来看一下自动装配的源码,看看Dubbo是如何进行自动装配的:
/**
* 自动装配扩展类实例
*/
private T injectExtension(T instance) {
try {
//如果objectFactory不为空,则进行自动装配
if (objectFactory != null) {
//遍历扩展类实例的所有方法
for (Method method : instance.getClass().getMethods()) {
//检测方法是否以set开头、只有一个参数、访问修饰符为public
if (isSetter(method)) {
//对于有@DisableInject的注解的setter方法,不需要注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
//获取setter方法的参数类型
Class<?> pt = method.getParameterTypes()[0];
//判断参数类型是否是原始类型(基本类型+String+基本类型的包装类+Date)
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
//获取要注入属性名,比如setName方法中对应的属性名为name
String property = getSetterProperty(method);
//从objectFactory中根据属性名与属性的Class类型获取依赖对象,
//获取到的是一个SPI接口的自适应扩展类的对象或者Spring环境下的一个bean,
//objectFactory.getExtension方法的细节我们在ExtensionFactory中将详细讨论
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
//调用setter方法进行注入
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method ...");
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
  对扩展类进行自动装配时,装配目标是名为“setXXX”的方法映射出来的属性,并且认为该属性的类型为setXXX方法的参数类型,属性名为“XXX”部分第一个字母小写之后加上其余字母。举个例子,有方法“setMar(Car foo)”,则要注入的属性类型为Car,属性名为mar。获取到装配目标的类型信息和名称信息后,调用objectFactory的getExtension方法获取属性的实例。      
  回忆一下,objectFactory这个对象,是getExtensionLoader方法中调用ExtensionLoader的私有构造器创建的,代码如下: 
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null :
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

  这几行简单的代码包含了很多信息:首先,ExtensionFactory自身也是一个SPI接口,其同样能够通过ExtensionLoader进行初始化。其次,当获取一个SPI接口的ExtensionLoader时,如果该SPI接口为ExtensionFactory,则不会设置objectFactory字段值,否则会通过getAdaptiveExtension方法拿到一个ExtensionFactory的实例并将字段objectFactory的值设置为它。

  在讲解扩展点自适应的代码编译小节中,我们分析了获取Compiler自适应扩展类实例的流程。同样的,在这里我们也可以照葫芦画瓢的来分析获取ExtensionFactory接口自适应扩展实例的流程。具体过程就不赘述了,最终获取到的自适应扩展实例是一个AdaptiveExtensionFactory实例对象。下面我们会花几个小节介绍一个ExtensionFactory相关的家族成员。

5.1.ExtensionFactory

  ExtensionFactory是一个SPI接口,其源码比较简单,包含一个方法用于根据Class以及name获取一个实例对象,方法名getExtension暗含了要获取的是扩展类对象。我们可以大胆猜测,这个方法应该是根据SPI接口Class和其配置文件中扩展类的name获取扩展类的实例。
@SPI
public interface ExtensionFactory {
<T> T getExtension(Class<T> type, String name);
}

  ExtensionFactory这个接口的继承树如下图所示:

 

  它有3个实现类,分别是AdaptiveExtensionFactory、SpiExtensionFactory、SpringExtensionFactory,相关的META-INFO配置文件内容如下:

#位于dubbo-common模块下的META-INFO/dubbo/internal/com.apache.dubbo.common.ExtensionFactory文件
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory #位于dubbo-spring模块下的META-INF/dubbo/internal/com.apache.dubbo.common.ExtensionFactory文件
spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory

5.2.AdaptiveExtensionFactory

  其实从配置文件中AdaptiveExtensionFactory的name可知,其应该为一个具有@Adaptive注解的扩展类作为自适应扩展类。
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory { //维护了一组ExtensionFctory接口扩展类的实例
private final List<ExtensionFactory> factories; //构造方法,获取所有ExtensionFactory配置的扩展类实例
public AdaptiveExtensionFactory() {
//加载ExtensionFactory对应的ExtensionLoader
ExtensionLoader<ExtensionFactory> loader =
ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();、
//getSupportedExtensions方法用于获取ExtensionLoader的cachedClasses缓存的keySet
//即SPI接口配置文件中name的Set集合
for (String name : loader.getSupportedExtensions()) {
//根据name名字,调用getExtension方法获取对应扩展类的实例并缓存
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
} @Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
//调用每一个ExtensionFactory实现类的getExtension方法,并返回第一个不为null的对象
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
} }

5.3.SpiExtensionFactory

  该Factory只支持具有@SPI注解的接口。
public class SpiExtensionFactory implements ExtensionFactory {

    @Override
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
//getSupportedExtensions方法用于获取ExtensionLoader的cachedClasses缓存的keySet
//即SPI接口配置文件中name的Set集合
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
} }

5.4.SpringExtensionFactory

  顾名思义,这个Factory应用在Spring环境下,是dubbo与Spring结合使用的ExtensionFactory。与SpiExtensionFactory的行为相反,它不支持@SPI注解的接口。
public class SpringExtensionFactory implements ExtensionFactory {
private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class); private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
private static final ApplicationListener SHUTDOWN_HOOK_LISTENER = new ShutdownHookListener(); public static void addApplicationContext(ApplicationContext context) {
CONTEXTS.add(context);
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context).registerShutdownHook();
DubboShutdownHook.getDubboShutdownHook().unregister();
}
//先SpringContext注册dubbo关闭钩子
BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER);
} //省略了一些CONTEXTS的CRUD方法... @Override
@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
//不支持被@SPI注解标注的接口类型
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
return null;
}
//首先根据name从Spring上下文获取bean,并验证该bean与type是否一致
for (ApplicationContext context : CONTEXTS) {
if (context.containsBean(name)) {
Object bean = context.getBean(name);
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
logger.warn("No spring extension (bean) named ...");
//如果type的类型为Object,直接返回null
if (Object.class == type) {
return null;
}
//尝试根据type从Spring上下文获取bean,如果type对应的bean并非唯一,直接报错
for (ApplicationContext context : CONTEXTS) {
try {
return context.getBean(type);
} catch (NoUniqueBeanDefinitionException multiBeanExe) {
logger.warn("Find more than 1 spring extensions (beans) of type ...");
} catch (NoSuchBeanDefinitionException noBeanExe) {
if (logger.isDebugEnabled()) {
logger.debug("Error when get spring extension(bean) for type: ...");
}
}
}
logger.warn("No spring extension (bean) named: ...");
//未找到,返回null
return null;
} private static class ShutdownHookListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook();
shutdownHook.doDestroy();
}
}
}
}

  SpringExtensionFactory维护了Spring上下文集合“Set<ApplicationContext>”。在getExtension方法中,通过Spring上下文去获取实例。并且,SpringExtensionFactory在设置SpringContext时,会向获取到的Context注册一个Spring容器关闭事件的监听钩子,用于关闭dubbo。

 

六、扩展点自动包装

  扩展点实例自动装配之后,便开始进行自动包装,包装的过程为遍历缓存“cachedWrapperClasses”并将装配好的扩展类实例作为包装扩展类的构造器参数,创建一个新的包装类实例,然后对这个新的实例进行自动装配。
  如何判定一个扩展类是否是包装类呢?在getExtension方法的源码分析中已经有提及,就是某个SPI接口的扩展类具有一个特定特征的构造函数,这个构造函数是单参数,并且参数类型是该扩展类实现的SPI接口类型。举个官网给出的例子:
package com.alibaba.xxx;

import org.apache.dubbo.rpc.Protocol;

public class XxxProtocolWrapper implements Protocol {
Protocol impl; //单参数构造函数,且参数类型为其实现的SPI接口类型Protocol
public XxxProtocolWrapper(Protocol protocol) { impl = protocol; } // 接口方法做一个操作后,再调用extension的方法
public void refer() {
//... 一些操作
impl.refer();
// ... 一些操作
}
}

  自动包装的机制能够近似的实现AOP的功能,通过Wrapper类可以把所有扩展点公共逻辑移至Wrapper中,新加的Wrapper在所有的扩展点上添加了逻辑。

 

七、扩展点自动激活

  还未完成。
 

dubbo源码分析01:SPI机制的更多相关文章

  1. dubbo源码分析1——SPI机制的概要介绍

    插件机制是Dubbo用于可插拔地扩展底层的一些实现而定制的一套机制,比如dubbo底层的RPC协议.注册中心的注册方式等等.具体的实现方式是参照了JDK的SPI思想,由于JDK的SPI的机制比较简单, ...

  2. dubbo源码分析4——SPI机制_ExtensionFactory类的作用

    ExtensionFactory的源码: @SPI public interface ExtensionFactory { /** * Get extension. * * @param type o ...

  3. dubbo源码分析5——SPI机制_AdaptiveExtension的原理和作用

    private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().n ...

  4. dubbo源码分析2——SPI机制中的SPI实现类的读取和预处理

    SPI机制中的SPI实现类的读取和预处理是由ExtensionLoader类的loadFile方法来完成的 loadFile方法的作用是读取dubbo的某个SPI接口的spi描述文件,然后进行缓存,缓 ...

  5. Dubbo源码分析之 SPI(一)

    一.概述 dubbo SPI 在dubbo的作用是基础性的,要想分析研究dubbo的实现原理.dubbo源码,都绕不过 dubbo SPI,掌握dubbo SPI 是征服dubbo的必经之路. 本篇文 ...

  6. Dubbo源码分析之SPI(二)

    一.概述 本篇文章是dubbo SPI源码分析的第二篇,接着第一篇继续分析dubbo SPI的内容,我们主要介绍 getDefaultExtension() 获取默认扩展点方法. 由于此方法比较简单, ...

  7. dubbo源码分析6——SPI机制中的AOP

    在 ExtensionLoader 类的loadFile方法中有下图的这段代码: 类如现在这个ExtensionLoader中的type 是Protocol.class,也就是SPI接口的实现类中Xx ...

  8. dubbo源码分析3——SPI机制中的ExtensionLoader类的objectFactory属性分析

    ExtensionLoader类是整个SPI的核心类,每个SPI都会对应一个ExtensionLoader类实例,这个类的构造方法如下: private ExtensionLoader(Class&l ...

  9. Dubbo源码分析之SPI(三)

    一.概述 本篇介绍自适应扩展,方法getAdaptiveExtension()的实现.ExtensionLoader类本身很多功能也使用到了自适应扩展.包括ExtensionFactory扩展. 通俗 ...

随机推荐

  1. SOA 架构与微服务架构的区别

    注重重用,微服务注重重写 SOA 的主要目的是为了企业各个系统更加容易地融合在一起. 微服务通常由重写一个模块开始.要把整个巨石型的应用重写是有很大的风险的,也不一定必要.我们向微服务迁移的时候通常从 ...

  2. ElementUI——动态表单验证

    前言 版本更新迭代的时候,需要用到一个动态表单的功能,ElementUI刚好有教程就改改用咯 步骤 代码 <!-- 手机副号动态表单框 --> <el-form-item v-for ...

  3. 使用eclipse-hadoop插件无法再eclipse操作(上传、删除文件)

    再conf中的hdfs-site.xml添加如下配置: <property><name>dfs.permissions</name><value>fal ...

  4. 使用vue-cli3搭建项目过程

    一.搭建前准备 node.js版本为8.9+: 安装模块:npm install -g n // 安装模块 这个模块是专门用来管理node.js版本的: 若原先已经安装,则更细模块:n stable ...

  5. Git的个人总结

    Git Git简史: 同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代. Linux 内核开源项目有着为数众多的参与者. 绝大多数的 Linux 内核维护工作都花在了提交补丁和保存 ...

  6. WinDbg常用命令系列---异常相关操作

    .exr (Display Exception Record) .exr命令显示异常记录的内容. .exr Address .exr -1 参数: Address指定异常记录的地址.如果指定-1作为地 ...

  7. Tomcat配置二级域名的分配与访问

    回顾tomcat Tomcat是Apache软件基金会(Apache Software Foundation)的一个顶级项目,由Apache, Sun和其他一些公司及个人共同开发,是目前比较流行的We ...

  8. CORS跨域资源共享总结

    1.CORS简述 CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing).它允许浏览器向跨源(协议 + 域名 + 端口)服务 ...

  9. c博客作业-我的第一篇博客

    1.你对网络专业或者计算机专业了解是怎样的? 以前接触计算机,只是把它当作娱乐的工具,并没有太过了解,现在我通过查阅了解了一些计算机的知识. 计算机专业的学生要学习的不仅是会使用,而且要学习计算机的基 ...

  10. vue Uncaught SyntaxError: Unexpected token < 报错

    这个问题是因为项目中出现没有闭合的标签,如<img src="">  需改成<img src="xxx.png"/>