顺着dubbo入口撸ExtensionLoader源码
参考
属于在这篇Dubbo扩展点加载基础上的展开学习。但原文有点小问题(Container启动那里),所以本文直接按自己的理解来组织。不加说明的引用都来自该文。
问题
Dubbo的原理、核心的概念很多文章都有详细的介绍(什么SPI、扩展点、Adaptive、ExtensionLoader等等),但是我的问题是它们是如何运行、如何起作用、整体的流程是什么?
1. Dubbo入口
执行Dubbo,实际上是执行了com.alibaba.dubbo.container.Main.main
Main这个类中有一些静态成员变量,最重要的是这个:
private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
从这里开始使用ExtensionLoader。
2. 初始化Main的静态成员,获得Container类型的ExtensionLoader
ExtensionLoader
类中有个静态的final变量EXTENSION_LOADERS
缓存了各种类型的ExtensionLoader
,如果已有缓存,就直接获取,如果没有就调用new方法新建。
启动时getExtensionLoader(Container.class)
获取Container
类型的ExtensionLoader
肯定不存在,去构造:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
在ExtensionLoader
的构造方法中可以看到,ExtensionLoader
有两个属性,一个是type
,标识了这个ExtensionLoader
所属的类型,另一个是ObjectFactory
,类型是ExtensionFactory
。
那么,ExtensionLoader<Container>
的this.type=Container.class
,
而根据构造函数,欲要得到Container.Class
的ObjectFactory
,须先得到ExtensionFactory
类型的ExtensionLoader
。
2.1 获取ExtensionFactory类型的ExtensionLoader
同样没有,去构造,根据构造函数ExtensionLoader<ExtensionFactory>
的this.type=ExtensionFactory.Class
,ObjectFactory=null
。
继续。
2.2 获取ExtensionFactory的扩展实现:getAdaptiveExtension()
Dubbo的微内核做得非常的彻底,ExtensionFactory也是一个扩展点,也需要通过ExtensionLoader<ExtensionFactory>加载
因为(Container
的)ObjectFactory
是ExtensionFactory
类型,而ExtensionFactory
是一个扩展点,那么就要用ExtensionFactory.Class
的ExtensionLoader
通过getAdaptiveExtension()
获取到ExtensionFactory.Class
的Adaptive实现,再给到(Container
的)ObjectFactory
。
在getAdaptiveExtension()
中,首先也是试图从ExtensionLoader<ExtensionFactory>
的一个缓存cachedAdaptiveInstance
中取,取不到就调用createAdaptiveExtension()
创建。
2.2.1 创建ExtensionFactory扩展: createAdaptiveExtension()
逻辑就一句:
injectExtension((T) getAdaptiveExtensionClass().newInstance());
先看 getAdaptiveExtensionClass()
。
2.2.1.1 获取ExtensionFactory扩展类:getAdaptiveExtensionClass()
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
这里整体的逻辑是,先getExtensionClasses()
,看看有没有已经存在的扩展实现类,有的话取到这些类,把标记了@Adaptive
的缓存到cachedAdaptiveClass
(比如说ExtensionFactory
就是有的),把没有标记@Adaptive
的扩展实现类缓存到cachedClasses
,返回。
如果没有的话,就调用createAdaptiveExtensionClass()
现生成code并compile一个Adaptive类出来,同样缓存到cachedAdaptiveClass
,返回。
Adaptive注解可以标记在类上,也可以标记在方法上。
如果Adaptive类选择扩展点实现的依据是根据上下文信息,即URL中的信息选择不同的实现,那么可以把Adaptive注解标记在服务的方法上,ExtensionLoader可以自动生成这种情况的Adaptive类。
如果是自己实现Adaptive类,那么需要在类上标记Adaptive注解,ExtensionLoader在加载扩展点时就能发现并创建Adaptive实现。
(来自Dubbo源码解析-2:Adaptive类,同样很厉害的文章嗯,本文对其也多有引用不一一列举了)
下面详细分析一下这两步。
2.2.1.1.1 获取已存在的扩展类:getExtensionClasses()
首先检查ExtensionLoader<ExtensionFactory>
的缓存cachedClasses
,有就get没有就调用loadExtensionClasses()
创建。
- loadExtensionClasses()
首先去拿SPI注解,如果注解不为null,获取注解的value:
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
...
String value = defaultAnnotation.value();
ExtensionFactory
定义如下:
@SPI
public interface ExtensionFactory {
/**
* Get extension.
*
* @param type object type.
* @param name object name.
* @return object instance.
*/
<T> T getExtension(Class<T> type, String name);
}
SPI注解类定义如下:
public @interface SPI {
/**
* 缺省扩展点名。
*/
String value() default "";
}
可以看到ExtensionFactory没有定义缺省扩展点名(后面会看到Container类有缺省定义)。
有缺省扩展点名就缓存到ExtensionLoader<ExtensionFactory>
的另一个缓存cachedDefaultName
,没有就继续。
继续,调用loadFile
装载扩展实现类。
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
- loadFile(Map<String, Class<?>> extensionClasses, String dir)
loadFile首先在指定目录下找名字是type的配置文件,然后读出来:
String fileName = dir + type.getName();
对 于 ExtensionFactory
来 说 , 会读到:
//META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
//META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
随后:
-
loadfile读入第一行数据,初始化:
name=adaptive, line = com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
。 -
使用
Class<?> clazz = Class.forName(line, true, classLoader)
加载类。 -
!type.isAssignableFrom(clazz)
:验证加载的类是否是当前type的一个实现。 -
clazz.isAnnotationPresent(Adaptive.class)
:检查如果设置了@Adaptive
,则把clazz
保存在cachedAdaptiveClass
中
对于ExtensionFactory
来说,AdaptiveExtensionFactory
设置了@Adaptive
注解:
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
...
}
- 对于没有设置
@Adaptive
的类,则存入loadExtensionClasses()
传到loadFile()
中的参数extensionClasses
,返回后在getExtensionClasses()
中赋给cachedClasses
缓存。
classes = loadExtensionClasses();
cachedClasses.set(classes);
对于ExtensionFactory
来说,SpiExtensionFactory
和SpringExtensionFactory
都没有设置@Adaptive
注解(同一个类只能有一个Adaptive
实现),所以都被存入了ExtensionLoader<ExtensionFactory>
的cachedClasses
。
2.2.1.1.2 动态生成没有自己实现的Adaptive类:createAdaptiveExtensionClass()
执行完getExtensionClasses()
,回到了2.2.1.1的getAdaptiveExtensionClass()
,如果此时cachedAdaptiveClass
仍为null
,说明没有找到标记了@Adaptive
的类,需要根据上下文动态生成。
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
动态生成的过程简单来说就是找到标记了@Adaptive
的方法,根据type通过StringBuilder
拼接一个类,加载Compiler
编译。
前面提到的参考文章(Dubbo源码解析-2:Adaptive类)里举了例子,可以看到生成了什么样子的代码。这里不详细说了。
2.2.1.2 为扩展注入依赖的其他扩展实现:injectExtension(T instance)
好的终于回来了,2.2.1.1节完成了getAdaptiveExtensionClass()
,并返回了cachedAdaptiveClass
。 为这个cachedAdaptiveClass
new了一个Instance以后,开始injectExtension
注入。
扩展点注入的代码如下,首先查找以set开头、带一个参数的public方法,如果set方法的名称大于3,则根据名称获取参数扩展点的名称,然后获取扩展点实现的实例,这可能又是一个次配置的解析和实例化过程。最后调用set方法将实例设置进去。
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")&&...) {
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) {
...
}
}
}
}
} catch (Exception e) {
...
}
return instance;
}
对于ExtensionFactory
来说,首先判断,if (objectFactory != null)
,则可以直接返回cachedAdaptiveClass
所new的instance
了。
2.2.2 ExtensionLoader<Container>构造完毕
整个2.2.1过程结束,拿到了ExtensionFactory.class
的扩展实现,也就是所返回的cachedAdaptiveClass
,即AdaptiveExtensionFactory
。
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
回到构造函数,ExtensionLoader<Container>
的objectFactory =AdaptiveExtensionFactory
。
ExtensionLoader<Container>
构造完毕。
3. 在main()中调用loader.getExtension()加载Container的扩展实现
Main函数的整体逻辑是,如果没有传入参数,就装载Container的SPI注解指定的默认Container,如果传入的参数(main函数参数、JVM启动参数、classpath下的dubbo.properties配置等)指定了Container,就装载通过参数指定的Container。
public class Main {
public static final String CONTAINER_KEY = "dubbo.container";
private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
...
public static void main(String[] args) {
try {
if (args == null || args.length == 0) {
String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
args = Constants.COMMA_SPLIT_PATTERN.split(config);
}
final List<Container> containers = new ArrayList<Container>();
for (int i = 0; i < args.length; i ++) {
containers.add(loader.getExtension(args[i]));
}
...
for (Container container : containers) {
container.start();
...
}
...
}
}
首先看Container类的定义:
@SPI("spring")
public interface Container {
/**
* start.
*/
void start();
/**
* stop.
*/
void stop();
}
它将SPI的value值设置Spring
,也就是指定默认的扩展实现名称是spring(默认情况只加载一个spring容器)。
再看关于Container的配置(以spring为例,其他jetty、log4j等都类似):
\\META-INF/dubbo/internal/com.alibaba.dubbo.container.Container
spring=com.alibaba.dubbo.container.spring.SpringContainer
而SpringContainer类没有加注解什么的,所以这里是通过SPI注解的默认值控制的,和ExtensionFactory的机制不同。
下面详细分析main函数加载Container扩展点过程。
3.1 如果没有传参,获得默认扩展名:loader.getDefaultExtensionName()
public String getDefaultExtensionName() {
getExtensionClasses();
return cachedDefaultName;
}
这里调用了getExtensionClasses()
,与2.2.1.1.1节介绍的过程相同。
不同的是,对于ExtensionLoader<Container>
,通过SPI注解定义了缺省扩展点名spring
,因此会将spring
缓存到cachedDefaultName
。
而由于Container的扩展实现类都没有设置@Adaptive
,则这些实现类都被缓存在cachedClasses
中,不会被缓存在cachedAdaptiveClass
。
3.2 获得Container:loader.getExtension()
- getExtension()
先去缓存cachedInstances
中找,如果没有,则调用createExtension
创建。
public T getExtension(String name) {
...
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;
}
- createExtension()
通过getExtensionClasses()
获得扩展实现类,调用injectExtension
注入依赖的其他扩展和包装类。返回获取了包装后的扩展instance。
private T createExtension(String name) {
Class<?> clazz = getExtensionClasses().get(name);
...
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
...
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
...
}
}
- getExtensionClasses()
其实是从获取之前load到cachedClassed中的所有扩展实现类。没有的话就调用loadExtensionClasses()
(见2.2.1.1.1节)获取。
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
...
classes = loadExtensionClasses();
...
}
return classes;
}
3.3 获取Container扩展后,启动
扩展加载结束,启动Container~
for (Container container : containers) {
container.start();
...
}