一.概览
整体描述
- dubbo利用spi扩展机制实现大量的动态扩展,要想充分了解dubbo的扩展机制,首先必须弄明白三个注解:
- @SPI: 被此注解标记的接口,便是是可扩展的接口,实现类便是动态抓取的了
- @Adaptive: 自适应扩展~ 该注解可以作用在“类”上,也可以作用在方法上,除了AdaptiveCompiler、AdaptiveExtensionFactory两个类之外,所有的都是注解在方法上。ExtensionLoader根据接口定义动态的生成适配器代码,并实例化这个生成的动态类。被Adaptive注解的方法会生成具体的方法实现。没备注街自然不能成为自适应了。
- @Activate: 激活扩展~ 可以注解在需要注解在类上或者方法上,并注明被激活的条件及指明排序信息。使用场景最多的就是dubbo的拦截器这一块了。
- 扩展核心类:ExtensionLoader 加载扩展点就是靠这个类了,加载方式后面详叙,先弄清楚几个主要的变量:
-
SERVICES_DIRECTORY: 定义SPI文件的扫描路径,dubbo源码中设置了好几个:META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/; 至于这些文件夹的文件格式如何看下图:
文件名为需要扩展接口全路径,文件内容为自配key-->实现类的全路径,很简单;
EXTENSION_LOADERS: 拓展点加载器的缓存 --> 接口类型和ExtensionLoader实例的映射关系
EXTENSION_INSTANCES: 拓展点的缓存
cachedClasses: 扩展文件中的 key --> class
cachedInstances: 扩展文件中的 key --> Hold<反射出来的intance>
cachedDefaultName: 接口SPI默认的实现名(就是把接口上填的默认值), 这个扩展优先级最高就在这里体现了
二.扩展点加载流程
1-获取ExtensionLoader
- 测试方法作为入口
@Test
public void testAdaptiveExtension() throws Exception {
SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getAdaptiveExtension();
Map<String, String> map = new HashMap<String, String>();
map.put("key", "impl_wxshi");
URL url = new URL("p1", "1.2.3.4", 1010, "path", map);
String echo = ext.yell(url, "haha");
assertEquals("xxxxx", echo);
}
- 方法内第一行很明显就是获取ExtensionLoader,进而进入主体:
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注解,否则报错
*/
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
/**
* 从缓存中获取
*/
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
/**
* 取不到创建一个放入EXTENSION_LOADERS中
*/
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
- 详细很简单:
- 1.判断要获取的扩展接口是注解@SPI
- 2.从缓存中获取EXTENSION_LOADERS,若没有直接创建并且放入缓存,即一类接口扩展点只需创建一次EXTENSION_LOADER
- 3.返回EXTENSION_LOADER用于下一步getAdaptiveExtension();
-
这里首次加载扩展点肯定会创建ExtensionLoader了,这里也是个很重要的点,跟着上面的代码EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));进入构造器:
/** * 默认初始化一个ExtensionFactory,这个类是用来获取扩展点实体的, * adaptive就是通过这个类取得扩展点,从而将请求委派给合适的实体完成请求 * Adaptive生成返回给用户后,用户对方法发起调用,adaptive通过getExtension取得provider。getExtension会通过cachedInstances判断此provider是否已cache。没有的话则通过createExtension生成。 */ private ExtensionLoader(Class<?> type) { this.type = type; /** * type如果是ExtensionFactory类型,那么objectFactory是null,否则是ExtensionFactory类型的适配器类型 */ objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
构造器里面又有一行这样的代码:ExtensionLoader.getExtensionLoader(ExtensionFactory.class);可想而知这里也是获取自适应点ExtensionFactory.class 用以构造ExtensionLoader了。
看看ExtensionFactory的接口和一个主要实现:
/**
* ExtensionFactory自身也是个扩展点,它被SPI注解
* 它有3个provider,分别是adaptive,spi和spring
* ExtensionFactory是主要是用来加载被注入的类的实现,分为SpiExtensionFactory和SpringExtensionFactory两个,分别用来加载SPI扩展实现和Spring中bean的实现。
*/
@SPI
public interface ExtensionFactory {
/**
* Get extension.
*
* 获取指定类型的指定名称的扩展点
*
* @param type object type.
* @param name object name.
* @return object instance.
*/
@Adaptive
<T> T getExtension(Class<T> type, String name);
}
/**
* AdaptiveExtensionFactory
* AdaptiveExtesnionFactory是dubbo自定义的adaptive,由于聚合了SpringExtensionFactory和SpiExtensionFactory,从而能对需要注入的对象的所有set方法进行注入
*/
@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;
}
}
看见@SPI了,同时重点关注下 @Adaptive注解就明确了,这个注解在上面讲了,如果在类上,优先级最高,那取得自适应点就是这个AdaptiveExtensionFactory了,干的事情看后面的作用就知道了,获取扩展点的流程我们用自己的接口解释,这里基本都是一致的,继续往后划重点。
2-自定义扩展点加载
-
主要进入测试方法中的第一句后半截getAdaptiveExtension()方法,看下图注释
/** * 对于扩展类型是ExtensionFactory的,设置为null * getAdaptiveExtension方法获取一个运行时自适应的扩展类型 * 每个Extension只能有一个@Adaptive类型的实现,如果么有,dubbo会自动生成一个类 * objectFactory是一个ExtensionFactory类型的属性,主要用于加载需要注入的类型的实现 * 这里记住非ExtensionFactory类型的返回的都是一个AdaptiveExtensionFactory */ @SuppressWarnings("unchecked") public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); if (instance == null) { 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("fail to create adaptive instance: " + t.toString(), t); } } } } else { throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } } return (T) instance; }
这里就是创建自定义自适应扩展点的入口了,很简单
- 1.从缓存中获取
- 2.获取不到直接创建,再加入缓存
-
直接进入 cachedAdaptiveInstance.set(instance);方法,创建扩展点
/** * 获取到适配器类的Class,利用反射创建适配器类的实例 * 如果需要自己声明一个adaptive,一定要有个无参构造方法。其实除了wrapper外的所有provider都必须要有个无参构造方法 */ @SuppressWarnings("unchecked") private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e); } }
-
此处主要点就出现了:Class<?> getAdaptiveExtensionClass();
private Class<?> getAdaptiveExtensionClass() { /** * 触发SPI流程的扫描 */ getExtensionClasses(); /** * 如果通过上面的步骤可以获取到cachedAdaptiveClass直接返回,如果不行的话,就得考虑自己进行利用动态代理创建一个了 * 什么情况下不为空?当扩展类上打上@Adaptive注解的时候,就会将这个类直接返回。如果没有上注解,怎么办,就得自己生成了,也就是createAdaptiveExtensionClass */ if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } /** * 利用动态代理创建一个扩展类 * 如果用户没有声明adaptive(cachedAdaptiveClass为空)则dubbo自动生成一个adaptive */ return cachedAdaptiveClass = createAdaptiveExtensionClass(); }
看注释一目了然,继续跟踪getExtensionClasses(),该方法没有返回值,从前面那么多缓存就可以看出,这个必然是出发了各种相关缓存的动作,为后面return作用。
-
继续看重点:SPI流程的扫描 getExtensionClasses();
/** 当cachedClasses为空时则开始load spi classes */ 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; }
风格跟之前的一个入口完全相似,先从缓存中取,没有就创建再加缓存,那重点就在加载处了;
-
继续跟进重点:loadExtensionClasses();
// synchronized in getExtensionClasses private Map<String, Class<?>> loadExtensionClasses() { /** * 获取到类型的SPI注解,所以利用SPI扩展点的地方,需要加入SPI注解 */ final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { 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 on extension " + type.getName() + ": " + Arrays.toString(names)); } /** * 如果注解中有value,说明有默认的实现,那么将value放到cachedDefaultName中 */ if (names.length == 1) cachedDefaultName = names[0]; } } /** * 开始load spi文件了,这是整个过程中最为关键也重要的一部 * 从下面的地址中加在这个类型的数据的extensionClasses中,地址包括 * META-INF/dubbo/internal/ * META-INF/dubbo/ * META-INF/services/ */ Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadDirectory(extensionClasses, DUBBO_DIRECTORY); loadDirectory(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
该方法做了两件事:
- 1.获取默认的注解值(即@SPI(XXX))中的这个XXX,并缓存;
- 2.扫描默认路径,加载扩展点还记得概览中的三个文件路径吗,这个就是主要的配置扩展点的文件路径了,dubbo会全局扫描所有该路径,遍历加载扩展点。至于加载出来的优先级,最后当然比不上类上配置@Adaptive了,但如果指明了key,那优先级也就不管了。
-
看loadDirectory(Map<String, Class<?>> extensionClasses, String dir)
/** * load spi文件 * dubbo spi的文件位置位于META-INF/dubbo/, META-INF/dubbo/internal和META-INF/services/,文件名就是接口class的full name * 对所有文件按行循环,读取的每行内容,以”=”号做分割,前面的是扩展点名称,后面的是实体处理类full name */ private void loadDirectory(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 resourceURL = urls.nextElement(); loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } }
加载资源,获取urls,遍历解析扩展类,放入缓存,跟核心方法
-
loadResource(extensionClasses, classLoader, resourceURL);
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8")); try { 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; int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { /** * Class.forName(line, true, classLoader)-->加载解析出来的类的全限定名称 */ loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } 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); } } } } finally { reader.close(); } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t); } }
无非就是解析配置项,获取扩展点类路径,通过类加载器加载,继续
-
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
/** 开始进入loadAdaptive */ private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { /** * 判断是新加载clazz是否是type的子类,不是报错 */ if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface."); } /** * 如果class上有@Adaptive,则是一个adaptive,此时开始下一个循环 * 注意这个点,查询type类型适配器类的时候会优先寻找cachedAdaptiveClass,因为是系统指定的适配器类,优先级最高 * */ if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } /** * 多个adaptive类的实例,报错 * 标准的适配器类只能有一个 */ else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName()); } } /** * 如果不是则开始loadWrapper。判断wrapper的标准是class有没有一个参数为接口类型的构造参数。如果是wrapper则塞入到cachedWrapperClasses中 * 不是wrapper时会抛出NoSuchMethodException异常,从而进入loadActivation和loadEntity的过程 * * 如果这个扩展类没有打上@Adaptive注解就更有意思了。 * 首先第一步会验证下有没有type这个类型作为入参的构造方法,为什么要这么做?因为Wrapper,有的类型需要包装一下,例如type=Protocol.class 就会看到有DubboProtocol真实的Protocal类 * 还会有ProtocolFilterWrapper和ProtocolListenerWrapper这种Wrapper类,这种Wrapper类的共同点就是构造函数的入参是type类型,所以在解析的时候有这么一步 * 如果有这种构造函数的就是Warpper类,将这些Warpper类型的数据放到cachedWrapperClasses这个集合中缓存 * 如果没有这种类型的构造函数,就是正常的type类型的实例了 * */ else if (isWrapperClass(clazz)) { Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } /** * 将此wrapper放入wrappers容器里面,也就是cachedWrapperClasses里面 */ wrappers.add(clazz); } /** 进入loadActivation和loadEntity的过程 */ else { /** 到了这里,说明是个正常的类 */ clazz.getConstructor(); /** * 1.处理name为null的情况,为空时截取provider的simpleName的一部分作为name。即:如果在文件中没有声明这个扩展的名称(=左边的部分),就会根据这个类名创建一个名称 * 2.上面提到过dubbo从三个文件夹,其中包括META-INF/services。熟悉jdk spi的对这个应该不陌生,这是jdk spi的默认加载路径,但是jdk声明provider只要声明一个full name就行。通过这一步dubbo spi就可以兼容jdk语法的spi了。 */ if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } /** * 看下这个类上有没有Activate注解 * 被@Activate声明的provider会被cache到cachedActivates里 * * * 然后进入下一个环节@Activate数据的解析。 * 1.查看type类上有没有@Activate注解,如果有的话,将名称与注解放到cachedActivates这个Map中进行缓存。 * 2.将扩展类和名称放入cachedNames这个Map中进行缓存,将名称和扩展类的class放入传递进来的extensionClasses中 * 3.最后这个extensionClasses会被返回出来被使用 */ String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { Activate activate = clazz.getAnnotation(Activate.class); //存在,就往cachedActivates里面添加名称与注解 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); /** * 非wrapper和adaptive的class都会被put到extensionClasses里,它是个map,这个map会被返回,然后被cache到cachedClasses。 */ if (c == null) { //最后往extensionClasses添加名称与class extensionClasses.put(n, clazz); } else if (c != clazz) { throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName()); } } } } }
注释很清楚,这里说明几个主要的点:
- 1.判断当前类上是否有@Adaptive注解,如果有则记录下,因为毕竟次注解优先级最高,但是该注解既然是默认优先级,因此肯定只能存在一个,多了就不知道找谁了。
- 2.是不是WrapperClass,从词义就知道这是一个包装类,其实类似于Spring的切面,将真正的扩展点类以构造方式包在其中,这样就可以实现一些额外逻辑了,即Wrapper 代理了扩展点。
- 3.当然是解析正常类了,现获取类名,判断是否有@Activate注解,加入缓存等。
到这里getExtensionClasses()就告一段落了,将刚才这里的出发点再贴一下,防止不能回头
private Class<?> getAdaptiveExtensionClass() {
/**
* 触发SPI流程的扫描
*/
getExtensionClasses();
/**
* 如果通过上面的步骤可以获取到cachedAdaptiveClass直接返回,如果不行的话,就得考虑自己进行利用动态代理创建一个了
* 什么情况下不为空?当扩展类上打上@Adaptive注解的时候,就会将这个类直接返回。如果没有上注解,怎么办,就得自己生成了,也就是createAdaptiveExtensionClass
*/
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
/**
* 利用动态代理创建一个扩展类
* 如果用户没有声明adaptive(cachedAdaptiveClass为空)则dubbo自动生成一个adaptive
*/
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
往下走就会发现,如果cachedAdaptiveClass != null,直接返回,这个就是体现优先级的地方了。若没有,继续深入createAdaptiveExtensionClass()
-
利用动态代理创建一个扩展类
/** * 将类以字符串的形式拼接出来,然后利用编译器进行编译,返回编译后的class对象 */ private Class<?> createAdaptiveExtensionClass() { /** * 创建代码的字符串形式 */ String code = createAdaptiveExtensionClassCode(); /** * 寻找类加载器 */ ClassLoader classLoader = findClassLoader(); /** * 寻找Compiler的适配器扩展类 */ com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); /** * 进行编译成Class的实例返回 */ return compiler.compile(code, classLoader); }
核心就在那个code的解释了,这里的code形式如下:
package com.alibaba.dubbo.common.extensionloader.ext1; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class SimpleExt$Adaptive implements com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt { public java.lang.String yell(com.alibaba.dubbo.common.URL arg0, java.lang.String arg1) { if (arg0 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg0; String extName = url.getParameter("key1", url.getParameter("key2", "impl1")); if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" + url.toString() + ") use keys([key1, key2])"); com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class).getExtension(extName); return extension.yell(arg0, arg1); } public java.lang.String echo(com.alibaba.dubbo.common.URL arg0, java.lang.String arg1) { if (arg0 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg0; String extName = url.getParameter("simple.ext", "impl1"); if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" + url.toString() + ") use keys([simple.ext])"); com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class).getExtension(extName); return extension.echo(arg0, arg1); } public java.lang.String bang(com.alibaba.dubbo.common.URL arg0, int arg1) { throw new UnsupportedOperationException("method public abstract java.lang.String com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.bang(com.alibaba.dubbo.common.URL,int) of interface com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt is not adaptive method!"); } }
code还是字符串形式,那后面的代码就清楚了
1.寻找类加载器
2.再利用自适应扩展点(javassist)寻找编译器Compiler
-
3.将字符串code生成具体的类,这个类就是最终的扩展点代理类啦。
对于第二点为啥是javassist,看下图:/** * Compiler. (SPI, Singleton, ThreadSafe) */ @SPI("javassist") public interface Compiler { /** * Compile java source code. * * @param code Java source code * @param classLoader classloader * @return Compiled class */ Class<?> compile(String code, ClassLoader classLoader); }
该@SPI("javassist")应该足以说明默认取的是哪个代理器了。
- 到此扩展点的代理就产生了,后面执行具体方法的时候,执行的实际是代理的方法体,再去执行具体逻辑,看字节码就清楚了执行过程。
3.执行具体的方法
-
String echo = ext.echo(url, "haha");其实执行的代理的如下方法体,看上图字节码
public java.lang.String echo(com.alibaba.dubbo.common.URL arg0, java.lang.String arg1) { if (arg0 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg0; String extName = url.getParameter("simple.ext", "impl1"); if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt) name from url(" + url.toString() + ") use keys([simple.ext])"); com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt extension = (com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.extensionloader.ext1.SimpleExt.class).getExtension(extName); return extension.echo(arg0, arg1); }
看到执行逻辑如下:
- 1.获取扩展点的Extensionloader,之前的流程中已经缓存了,所以直接获取
- 2.调用getExtension(extName)方法获取具体的扩展点,这个就是最后获取真正代理的时候了
-
获取实际扩展点的真正代理
/** * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException} * will be thrown. */ @SuppressWarnings("unchecked") public T getExtension(String name) { if (name == null || name.length() == 0) 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(); if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { instance = createExtension(name); /** 缓存 */ holder.set(instance); } } } return (T) instance; }
重点代码就是instance = createExtension(name); 返回instance,且缓存。继续跟踪:
/** * 1.getExtensionClasses在cachedClasses有值时直接返回cachedClasses。上一节介绍过,loadFile结束后会把provider calss都cache到cachedClasses上 * 2.通过cachedClasses就可以获得name对应的provider了 * 3.国际惯例还是要inject * 4.存在wrapper的话则迭代包装,上一节也介绍过,先定义的包装后定义的。 */ @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 (wrapperClasses != null && !wrapperClasses.isEmpty()) { 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); } }
风格完全不变,依然先尝试从缓存中获取,没有则EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
这里就根据具体的class newInstance()出一个对象了,这个对象就是指定的具体的实际调用对象了。
这里还有一个小的点,就是注释描述的那样,inject + wrapper,wrapper前面介绍过,inject如下:/** * 如果适配器类有属性的set方法,会自动注入 * 生成provider后 通过injectExtension对其进行注入。注入是通过ExtensionFactory完成的 * */ 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())) { 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) { 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; }
最后返回的instance就执行具体的方法。