前言
我们在设计程序的时候 要考虑到扩展性 以应对不断变化 ,让应用程序接口和具体实现的完全解藕。dubbo 所有的功能组件都拥有良好的扩展性,得益于使用SPI 动态加载不同的扩展点实现类。作为SPI的核心实现 ,ExtensionLoader 能够在运行时生成扩展实现类,并能够自适应扩展,获取自动激活扩展类。下面就讲讲ExtensionLoader 的工作原理
ExtensionLoader 中获取扩展点有三种形式 分别为
1 getExtension(String name) --获取普通扩展类
2 getAdaptiveExtension() -- 获取自适应看扩展类
3 getActivateExtension(URL url, String key) --获取自动激活扩展类
下面分别讲述三种获取扩展点的实现
获取普通扩展类 --getExtension(String name)
image.png下面看看 getExtension(String name) 每一步操作
1 根据传入参数扩展点名 判断是否缓存是否已存在该扩展点
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
// 判断缓存中是否存在扩展点实例
if (instance == null) {
// 扩展点不存在 则调用createExtension 创建
instance = createExtension(name, wrap);
holder.set(instance);
}
}
}
2 创建扩展点
在createExtension中首先是根据不同路径读取扩展点描述信息
路径有
META-INF/dubbo/external/
META-INF/dubbo/internal/
META-INF/dubbo/
META-INF/services/
其中META-INF/services/ 兼容了java spi的默认路径
createExtension 调用了 getExtensionClasses来加载不同路径下的扩展点信息 我们看下此方法及其内部调用的 loadExtensionClasses
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 此处为加载扩展点class
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
// 获取并缓存默认的实现类
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
// strategies 为包含不同路径信息实例化类集合
for (LoadingStrategy strategy : strategies) {
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
}
return extensionClasses;
}
全局静态变量strategies 由方法 loadLoadingStrategies 解析LoadingStrategy文件并数组实例化生成
// 解析加载策略化类实例 返回数组
private static LoadingStrategy[] loadLoadingStrategies() {
return stream(load(LoadingStrategy.class).spliterator(), false)
.sorted()
.toArray(LoadingStrategy[]::new);
}
loadDirectory方法 根据LoadingStrategy生成对应的扩展点class信息
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls = null;
// 返回当前 类加载器 ExtensionLoader
ClassLoader classLoader = findClassLoader();
// try to load from ExtensionLoader's ClassLoader first
if (extensionLoaderClassLoaderFirst) {
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
urls = extensionLoaderClassLoader.getResources(fileName);
}
}
if (urls == null || !urls.hasMoreElements()) {
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
// 加载生成class信息
loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
loadResource 加载class信息
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
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;
// 配置文件内容格式 xxx=包名.类名
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0 && !isExcluded(line, excludedPackages)) {
// 再次调用 加载类信息
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name, overridden);
}
} 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);
}
}
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
loadClass 保存class信息
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
boolean overridden) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
if (clazz.isAnnotationPresent(Adaptive.class)) {
// 注释了自适应扩展类 缓存自适应扩展类
cacheAdaptiveClass(clazz, overridden);
} else if (isWrapperClass(clazz)) {
// 缓存包装类
cacheWrapperClass(clazz);
} else {
clazz.getConstructor();
if (StringUtils.isEmpty(name)) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
cacheActivateClass(clazz, names[0]);
for (String n : names) {
// 缓存类名和class的对应关系
cacheName(clazz, n);
// 保存扩展类class信息
saveInExtensionClass(extensionClasses, clazz, n, overridden);
}
}
}
}
loadExtensionClasses 方法中还有 cacheDefaultExtensionName 获取并缓存默认的实现类 默认实现类 使用 @SPI注释 如dubbo 默认传输协议使用 netty
@SPI("netty")
public interface Transporter {
private void cacheDefaultExtensionName() {
//
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation == null) {
return;
}
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));
}
if (names.length == 1) {
cachedDefaultName = names[0];
}
}
}
3 实例化扩展类
实例化并依赖注入其他扩展类
injectExtension(instance);
包装类注入扩展类
if (wrap) {
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
}
扩展类初始化
initExtension(instance);
获取自适应扩展类 --getAdaptiveExtension(String name)
getAdaptiveExtension会为扩展点接口自动生成实现类字符串, 有@Adaptive 注解的方法生成默认实现,默认实现根据url中提取的Adative参数 动态加载扩展点,再根据不同的编译器编译成class文件返回
private Class<?> createAdaptiveExtensionClass() {
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
大致分为4步
1 生成头部信息 保护 import package等
private String generatePackageInfo() {
return String.format(CODE_PACKAGE, type.getPackage().getName());
}
private String generateImports() {
return String.format(CODE_IMPORTS, ExtensionLoader.class.getName());
}
2 生成默认实现类名称,如果@Adaptive 中没有设置默认值 则根据类名称生成
private String generateClassDeclaration() {
return String.format(CODE_CLASS_DECLARATION, type.getSimpleName(), type.getCanonicalName());
}
3 循环生成方法
for (Method method : methods) {
code.append(generateMethod(method));
}
- 生成调用结果代码
compiler.compile(code, classLoader);
获取自动激活扩展类 --getActivateExtension(URL url, String key, String group)
逻辑就是根据url中指定的参数 自动激活对应的扩展类
getActivateExtension根据url参数配置 获取要激活的扩展类信息 并按顺序排序 最终由 获取普通扩展类 getExtension 依次生成
代码相对简单 这里不再贴上 有兴趣的可以参考 源码
org.apache.dubbo.common.extension.ExtensionLoader#getActivateExtension(org.apache.dubbo.common.URL, java.lang.String[], java.lang.String)
完
网友评论