深入dubbo内核(1):dubbo的自动发现机制

首先了解一下java的spi

  • spi的设计目标:

    面向对象的设计思想是模块之间基于接口编程,模块之间不可以对实现类进行硬编码,一旦代码涉及了具体的实现类就违反了可插拔的原则,如果要替换一种实现就要修改代码。

  • Java spi提供了这样一种机制:

    为某个接口寻找服务实现的机制,类似于IOC的思想,将装配的控制权转移到代码之外。

  • Java spi的具体约定:

    当服务的提供者(provider),提供了一个接口多种实现的时候,一般都会在jar包的/META-INF/services/目录下创建该接口的同名文件,文件的内容是该接口的具体实现类名称,而当外部加载这个模块的时候就可以通过jar包的/META-INF/services/中的配置文件得到具体的实现类,并且实例化,完成模块的装配。

  • dubbo的自动发现机制基于jdk标准的spi进行了扩展:

    JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源

    增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点

  • dubbo spi的具体约定:

    spi文件存储路径在/META-INF/dubbo/internal目录下,并且文件名为接口全路径名。文件内定义格式为:扩展名+具体类名

  • 扩展点加载使用的注解

/**
*扩展点接口必须有SPI注解
*/

com.alibaba.dubbo.common.extension.SPI
/**
*扩展点自适应装配
*/

com.alibaba.dubbo.common.extension.Adaptive
/**
*扩展点自动激活
*/

com.alibaba.dubbo.common.extension.Activate
/**
*dubbo使用ExtensionLoader类实现扩展点的自动加载
*/

com.alibaba.dubbo.common.extension.ExtensionLoader
  • getExtensionLoader()返回扩展点ExtensionLoader并载入缓存
public class ExtensionLoader<T> {
// 缓存ExtensionLoader
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
/**
* 获得扩展点的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!");
}
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) {
// 如果key存在则不替换value
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
}
  • 构造方法
 /**
* 扩展点class对象
*/

private final Class<?> type;
/**
* 管理所有的扩展点,用于IOC和AOP
*/

private final ExtensionFactory objectFactory;
private ExtensionLoader(Class<?> type) {
this.type = type;
// 在这里会加载ExtensionFactory自适应扩展点
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
}

在构造方法中同样使用扩展点加载为objectFactory赋值,用于IOC.并且EXTENSION_LOADERS是一个ConcurrentHashMap,保证ExtensionLoader的单例。

  • getAdaptiveExtension()获取自适应扩展装饰类对象
 public class Holder<T> {
private volatile T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
/**
* 获取自适应扩展装饰类对象
*/

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;
}

加载自适应扩展点的时候同样使用了缓存,并且使用了单例模式,但是在这里使用了一个Holde对象,Holder的value是被Volatile关键字修饰的,这样做是为了避免在使用单例模式的时候返回null。
单例模式为什么要使用Volatile关键字修饰:http://mp.weixin.qq.com/s/2UYXNzgTCEZdEfuGIbcczA

 /**
* 创建自适应扩展点实例
* @see #injectExtension(java.lang.Object) 依赖注入
*/

private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
}

获得自适应扩展点的实例后,injectExtension()用于对自适应扩展点进行依赖注入。

 private volatile Class<?> cachedAdaptiveClass = null;
/**
* 加载自适应扩展点Class对象
*/

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

这里会首先加载扩展点所有实现类的Class对象,然后判断cachedAdaptiveClass 是否为null,做不同的操作。

  • getExtensionClasses() 加载扩展点所有实现类的Class对象
// 缓存所有扩展点实现类的class对象
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
/**
* 加载扩展点实现类Class对象到缓存
* Map<String, Class<?>> key 扩展点实现类名称 value扩展点实现类Class对象
* 同样使用单例模式,map维护在Holder中
*/

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;
}
/**
* 扩展点加载路径
*/

private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
/**
* 加载扩展点实现类Class对象
*
*/

private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if (value != null && (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));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}

loadFile是正真加载所有点实例到缓存的方法。

  • loadFile() 缓存所有扩产展点实现类的class对象
// 自适应扩展点实例
private volatile Class<?> cachedAdaptiveClass = null;
// 自动激活
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
// 扩展点实现类缓存
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
private void loadFile(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 url = urls.nextElement();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
try {
String line = null;
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对象
Class<?> clazz = Class.forName(line, true, classLoader);
// 扩展点实现类必须是该扩展点的子类
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.");
}
/**
* @see com.alibaba.dubbo.common.extension.Adaptive
* <li>如果扩展点实现类上存在 Adaptive 注解 ,则缓存起来。<li/>
* <li>一个扩展点实现类只允许存在一个,Adaptive 注解在类上的实现类</li>
*/

if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else {
/**
* 如果扩展点实现类是对扩展点的一个装饰类
* 加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper类。
* 允许有多个装饰类
*/

try {
clazz.getConstructor(type);
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
clazz.getConstructor();
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
if (clazz.getSimpleName().length() > type.getSimpleName().length()
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
} else {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
}
}
}
String[] names = NAME_SEPARATOR.split(name);
/**
* 目前dubbo中所有的扩展点名称和实现类都是一一对应的不存在一对多的关系
*/

if (names != null && names.length > 0) {
/**
* 缓存实现类有 Activate 注解 的扩展点实现类的 value
*/

Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
/**
* 缓存扩展点实现类,class对象做为key
*/

if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
/**
* 缓存扩展点实现类,name对象做为key
*/

Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
} // end of while read lines
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + url + ") in " + url, t);
}
} // end of while urls
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}

至此扩展点的实现类都会被缓存起来。但是对于有Adaptive,Active注解的扩展点实现类会有不同的处理。并且一个扩展点接口只允许有一个Adaptive注解在类上的实现类。
观察getAdaptiveExtensionClass()方法会发现如果Adaptive注解上方法上,会动态代理生成一个XXX$Adaptive的代理类,里面是一个非常复杂的过程。

  • 总结:dubbo的spi中大量的运用了缓存,单例的设计模式,装饰模式和充分的考虑了并发问题,值得我们借鉴。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值