一、dubbo启动流程
了解dubbo内核之前,我们先看下dubbo的启动流程,参考了本地启动时日志打印的dubbo启动整理出来的流程图:
(1)启动provider时,会把所有暴露的接口注册到注册中心,并会订阅动态配置(configuration)。
(2)启动consumer时,会订阅服务端接口、动态配置、负载均衡集群(providers、configuration、routers)。
(3)provider:订阅的动态配置信息变更时,注册中心会推送动态配置信息(configuration)给provider。
consumer:订阅内容变更时,会推送订阅的信息(providers、configuration、routers)给consumer。
(4)客户端与服务端建立长连接,进行数据通讯。
(5)客户端与服务端启动后,后台会启动定时器,每隔一定的时间发送统计数据给monitor。
二、JDK标准的-SPI
在面向对象中,模块之间都是基于接口的,模块之间如果其中一个模块或接口实现需要进行更改,则就需要修改代码。为了实现不对模块或实现类之间不进行硬编码,即不在模块里写死代码,就可以使用spi这种服务发现机制。jdk中就提供了这种标准的spi机制,将模块实现移到代码之外。
· jdk-spi的具体约定
当服务的提供者提供了一个接口多种实现时,一般在jar包的META-INF/services/目录下,创建该接口的同名文件,该文件里的内容就是该服务接口的具体实现类的名称。
当外部加载这个模块的时候,就能通过jar包META-INF/services/里的配置文件得到具体的实现类名,并加载与实例化,完成模块之间的装配。
· jdk中使用spi的例子
mysql的Driver驱动的加载就使用到了spi机制。如下图,在META-INF中配置了Driver的实现类名为:com.mysql.cj.jdbc.Driver。
代码语言:javascript复制com.mysql.cj.jdbc.Driver
jdk的spi通过ServiceLoader.load进行加载。虽然Driver的接口很多,但是META-INF中配置的类名只有一个,因此只会对com.mysql.cj.jdbc.Driver实现类进行加载。
代码语言:javascript复制ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
· jdk标准的SPI机制的缺点
JDK标准的SPI会一次性实例化扩展点所有实现,因此不管是否用到这些实现类都会加载META-INF中配置的所有实现类,比较耗时。
dubbo内核中,也使用了spi机制,但是内部对spi的实现进行了修改来保证性能。
三、dubbo内核-SPI
· dubbo-spi的具体约定
spi文件的存储路径在META-INF/dubbo/internal目录下。
spi文件内容定义为:扩展名=具体的类型。如下图所示:
代码语言:javascript复制dubbo=org.apache.dubbo.registry.dubbo.DubboRegistryFactory
dubbo-spi比jdk-spi的内容定义中多了扩展名,即dubbo对spi的实现类进行加载时,会根据key即扩展名对需要用到的spi实现类进行加载。
spi实现类则是通过ExtensionLoader进行加载。
· spi实现路径
(1)getExtensionLoader获取ExtensionLoader。
可以通过ExtensionLoader.getExtensionLoader(Class<T> type)来为type接口new一个ExtensionLoader,并缓存起来。
我们可以看dubbo容器Container启动时的main方法中的关键一行代码:
代码语言:javascript复制public class Main {
private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
代码语言:javascript复制public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//... //判断缓存中是否存在 loader ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) { //new出一个loader,到缓存中 EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
· ExtensionLoader构造函数
代码语言:javascript复制private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class) .getAdaptiveExtension());
}
ExtensionLoader内部存放了type即Container接口名,与objectFactory即ExtensionFactory,后续可通过extensionFactory获取Container接口的实现。
ExtensionLoader.getExtensionLoader(Class<T> type)执行完毕,就表示Container接口的ExtensionLoader被创建了出来并被放置在了缓存中,并且ExtensionLoader内部完成了type和extensionFactory属性的初始化。
· ExtensionFactory的作用
可通过getExtension为dubbo的IOC提供所有对象。并且ExtensionFactory的objectFactory为null。
(2)getAdaptiveExtension获取扩展装饰类对象
被@Adaptive注解修饰的类就是扩展装饰类,点开注解后我们可以看到,@Adaptive注解只能修饰类和方法。
阅读getAdaptiveExtension源码:
代码语言:javascript复制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;
}
上述代码主要为从缓存中获取adaptiveExtension,如果不存在则创建,创建成果后再放入到缓存中。
· createAdaptiveExtension
代码语言:javascript复制private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension ");
}
}
(1)getAdaptiveExtensionClass
代码语言:javascript复制
代码语言:javascript复制private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
第一步:getExtensionClasses中会加载SPI中的实现类。
代码语言:javascript复制private Map<String, Class<?>> loadExtensionClasses() {
//...//加载META-INF/dubbo/internal/接口名 SPI文件中的实现类。 loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
return extensionClasses;
}
代码语言:javascript复制private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
//... //SPI实现类 是否包含adaptive注解 if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) { //对cachedAdaptiveClass赋值 cachedAdaptiveClass = clazz;
//... //构造函数中是否包含目标接口 } else if (isWrapperClass(clazz)) {
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
} //存在这样的实现类则放到wrappers中 wrappers.add(clazz);
} else {
//...
if (names != null && names.length > 0) { //包含activate注解的实现类存放在cachedActiviates中 Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
} else {
com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
if (oldActivate != null) {
cachedActivates.put(names[0], oldActivate);
}
}
for (String n : names) { //剩余的类就放在cachedNames if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
//...
}
}
}
}
上述源码中会加载type-目标接口位于META-INF/dubbo/internal/接口名下的SPI实现类,并会根据对应策略将实现内容放在不同的缓存变量中。
如果实现类中包含了adapive注解修饰的类,则会把该目标接口放到cachedAdaptiveClass中,例如ExtensionFactory。
如果实现类中的构造函数中包含了目标接口,则会将实现类放到cachedWrapperClasses集合中。
如果实现类类中包含了activate注解修饰的类,则会把实现类放到cachedActivates中。
剩余其他的类,则放在cachedNames中。
代码语言:javascript复制private Class<?> getAdaptiveExtensionClass() { //加载目标接口的SPI实现类
getExtensionClasses();
if (cachedAdaptiveClass != null) { //cachedAdaptiveClass在加载时被赋值了则直接返回 return cachedAdaptiveClass;
} //否则创建
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
(2)createAdaptiveExtensionClass
生成一个动态的adaptive类。
代码语言:javascript复制private Class<?> createAdaptiveExtensionClass() {
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler= ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
这里和动态代理有点相似,生成了一个动态的adaptive类。类名则为<目标接口名>$Adaptive implements <目标接口>。
动态类中的方法,只有方法被@Adaptive修饰的方法才会实现。没有被修饰的方法则无法实现。
· injectExtension
代码语言:javascript复制private T injectExtension(T instance) {
//...
Object object = objectFactory.getExtension(pt, property);
代码语言:javascript复制 //...
代码语言:javascript复制 return instance;
}
在injectExtension中进行dubbo的IOC,本质上就是从spi和spring中获取对象进行赋。
SpiExtensionFactory.getExtension和SpringExtensionFactory.getExtension。
@Adaptive注解总结
官方文档中解释为:Adaptive可注解在类或方法上。当注解在类上时,dubbo不会为该类生成代理类,表示扩展的加载逻辑由人工编码完成。当注解在方法上时,dubbo会为该方法生成代理逻辑,表示拓展的加载逻辑需由框架自动生成。
(1)注解在接口上
在调用getAdaptiveExtension方法时,直接返回该类,然后执行IOC。
(2)注解在方法上
在调用getAdaptiveExtension方法时,会生成代理类,然后执行IOC。代码模板如下:
(3)getExtension获取对象
代码语言:javascript复制public T getExtension(String 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;
}
代码语言:javascript复制private T createExtension(String name) { //从缓存中获取class对象,即对META-INF进行加载时会放到缓存中 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);
} //进行dubbo的IOC set注入 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);
}
}
四、dubbo-spi实现路径总结
(1)通过getExtensionLoader获取ExtensionLoader。
(2)通过getAdaptiveExtension获取扩展装饰类对象,动态生成装饰类或动态代理类,进行依赖注入。
(3)最后可通过ExtensionLoader.getExtension(name)获取对象。
就是这么简单。
· getExtension中所用到的设计模式
(1)被@Adaptive修饰的类,使用了装饰者模式。
(2)被@Adaptive修饰的方法,生成了动态的Adaptive类,使用了动态代理模式。
(3)使用缓存确保只生成一个类,使用了单例模式。
(4)objectFactory将对象new出来的过程隐藏在工厂中,使用了工厂模式。
(5)injectExtension中使用了IOC,还有资料说试用了AOP,这个AOP我没看出来。