ExtensionLoader



从上图中看到该类的构造方法被私有化,并且提供了一个静态方法来获取实例对象,

是的,该类使用了单例模式,懒汉模式

ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS...  key:被扩展类,value:扩展器

一、构造方法:

  • 将当前接口类型赋值给 this.type
  • 将AdaptiveExtensionFactory对象赋值给 objectFactory对象
// 构造方法
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
.getAdaptiveExtension());
}

二、获取对象的静态方法

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("扩展类必须存在");
if (!type.isInterface()) {
throw new IllegalArgumentException("需要扩展的类型必须是接口");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("被扩展的类必须被@SPI修饰");
}
// 从缓存中获取
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
// 缓存中没有,创建一个
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}

可以得出结论:

被扩展类必须是一个被@SPI注解修饰的接口

​ dubbo先从缓存中获取扩展类的实例,如果没有则通过单例的懒汉式创建ExtensionLoader对象, 然后放入缓存

三、现在来看看下面这句代码, 创建当前类型的类扩展器,并放入缓存

EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));

然后是构造方法,调用构造方法时,一个新的上下文,注意其中给objectFactory赋值的语句

private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

注意:getAdaptiveExtension并没有为ExtensionFactory类型生成动态类, 在getAdaptiveExtensionClass方法中有控制(因为当前接口对应的扩展类文件中有AdaptiveExtensionFactory类,给cachedAdaptiveClass赋值了)

本类中的属性

private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
// 扩展文件位置一
private static final String SERVICES_DIRECTORY = "META-INF/services/";
// 扩展文件位置二 这两个一般用来自定义扩展
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
// 扩展文件位置三
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
// 扩展名分割
private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
// 扩展接口 与对应的扩展器(存放所有的扩展器)
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
// 具体扩展类(class)与其实例对象(new 之后的,存放所有的实例)
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>(); // ==============================
//被扩展的具体接口
private final Class<?> type;
// 当前接口对应的文件内@Adaptive标识的对象(被IOC注入过) 用于获取扩展对象
private final ExtensionFactory objectFactory;
// key:扩展类型 value: 扩展名 用于通过接口字节对象(class对象)获取其扩展名
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
// 除了@Adaptive和AOP扩展类之外所有的扩展类
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>(); // 被@Activate注解修饰的扩展类 k:扩展名 v:扩展实例 eg:在获取Filter时有用到
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
// k: 扩展名, value: 包含扩展实例的 Holder对象
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>(); // 持有动态扩展类的holder对象 调用getAdaptiveExtension方法才会有值
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>(); //被@Adaptive注解修饰的扩展类
private volatile Class<?> cachedAdaptiveClass = null; // 被扩展类的@SPI注解中的value数组的第一个value[0]
private String cachedDefaultName; //创建动态类是的异常对象
private volatile Throwable createAdaptiveInstanceError;
// 扩展类的AOP增强类
private Set<Class<?>> cachedWrapperClasses; //加载扩展类配置文件出错的信息
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();

getAdaptiveExtension

获取动态代理类

public T getAdaptiveExtension() {
//从缓存中获取动态扩展类
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//1、创建 2、放入到
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
...
}
}
}
} else {
...
}
} return (T) instance;
}

createAdaptiveExtension

这个类用来给动态类注入属性(IOC) ,包含了@Adaptive修饰的类

private T createAdaptiveExtension() {
try {
// 给创建的动态类注入属性(IOC)
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
...
}
}

getAdaptiveExtensionClass 获取动态类

private Class<?> getAdaptiveExtensionClass() {
//加载动态类
getExtensionClasses();
//如果存在@Adaptive注解修饰的扩展类,直接返回(被它修饰的类是个固定的类,不需要生成动态类)
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 创建动态类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

getExtensionClasses

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

用来加载指定扩展文件下,文件名与当前接口的限定名相同的文件,里面的所有扩展类

private Map<String, Class<?>> loadExtensionClasses() {
//获取接口
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
//这个if操作是将接口@SPI注解上的值赋值给cachedDefaultName
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw ...
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
// 这个map主要用来从三个固定目录下获取 与当前接口的全限定名相同的文件里面的所有扩展类
// 比如 接口名为per.qiao.service.TestService 那么就会加载下面三个路径下的扩展类
//META-INF/services/per.qiao.service.TestService
//META-INF/dubbo/per.qiao.service.TestService
//META-INF/dubbo/internal/per.qiao.service.TestService
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}

loadDirectory

加载一个路径下的扩展类

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) {
...
}
}

loadClass

加载一个文件内的扩展类, 一个接口在同一个文件里只能有一个扩展类被@Adaptive修饰

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
throw ...
}
//如果当前文件中的扩展类有@Adaptive修饰,赋值给cachedAdaptiveClass
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
//如果同一文件内有多余一个的类被Adaptive修饰,抛异常;
}
} else if (isWrapperClass(clazz)) {
//如果当前扫描的类有个构造方法,并且该构造方法的参数与当前类型相同(type),保存到 cachedWrapperClasses,这里是实现AOP的一个关键点
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} else {
//扩展类要有无参构造
clazz.getConstructor();
//校验扩展名, 这里可以发现扩展名可以为空 即配置文件不使用key=value,学JDK直接使用value
// 那么1.该扩展类上有@Extension注解 或者2.该类的名字的末尾包含当前类的名字
// 即: 如果当前类叫 Qiao 那么该类的名字为 AbcQiao 那么这个类的扩展名就是abc
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw ...
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
//将被@Activate修饰的类缓存到cachedActivates中
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
//将@Adaptive, AOP的Wrapper除外的其他本文件下的扩展类缓存到cachedNames
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
//将@Adaptive, AOP的Wrapper除外的其他本文件下的扩展类添加到extensionClasses(map)
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new ...
}
}
}
}
}

createAdaptiveExtensionClass

private Class<?> createAdaptiveExtensionClass() {
//创建动态类代码
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
//获取编译器
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//编译动态代码
return compiler.compile(code, classLoader);
}

ExtensionLoader的更多相关文章

  1. dubbo源码学习(一)之ExtensionLoader

    [转载请注明作者和原文链接,欢迎讨论,相互学习.] 一.前言 ExtensionLoader类,主要是根据扩展点名称来对扩展点接口实现进行的一系列操作,如果获取扩展点接口实现实例.适配类实例.更新实现 ...

  2. Dubbo源码学习--环境搭建及基础准备(ServiceLoader、ExtensionLoader)

    环境搭建 Github上下载Dubbo最新发布版本,楼主下载版本为2.5.7. cd到源码解压目录,maven编译,命令为: mvn clean install -Dmaven.test.skip 生 ...

  3. dubbo源码分析3——SPI机制中的ExtensionLoader类的objectFactory属性分析

    ExtensionLoader类是整个SPI的核心类,每个SPI都会对应一个ExtensionLoader类实例,这个类的构造方法如下: private ExtensionLoader(Class&l ...

  4. dubbo的ExtensionLoader

    了解4个概念:接口,实现类,wrapper,adaptive. 扩展是接口实现类被wrap之后的对象,adaptive扩展是动态生成的类(例如Dubbo$Adaptive类). dubbo框架为接口指 ...

  5. 从ExtensionLoader理解Dubbo扩展机制

    Dubbo的扩展机制是怎么实现的?最简单的回答就是@SPI. Dubbo的插件化思路来源于Java SPI.   JAVA SPI 机制     SPI的全名为Service Provider Int ...

  6. Dubbo源码分析之ExtensionLoader加载过程解析

    ExtensionLoader加载机制阅读: Dubbo的类加载机制是模仿jdk的spi加载机制:  Jdk的SPI扩展加载机制:约定是当服务的提供者每增加一个接口的实现类时,需要在jar包的META ...

  7. Dubbo源码分析(5):ExtensionLoader

    背景 Dubbo所有的模块加载是基于SPI机制的.在接口名的上一行加个@SPI注解表明要此模块要通过ExtensionLoader加载.基于SPI机制的扩展性比较好,在不修改原有代码,可以实现新模块的 ...

  8. 变通实现微服务的per request以提高IO效率(三)

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  9. 0032 Java学习笔记-类加载机制-初步

    JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...

随机推荐

  1. vue怎么将一个组件引入另一个组件?

    项目是由的vue-cli搭建 1.这里有两个组件,需求是把newComponents.vue里面的东西引入到helloWorld里面 2.index.js里面的配置 3.newComponents里面 ...

  2. js小脚本解析后台数据

    java代码 List<CodeTableBean> clfsList = StandardCodeTable.getCodeTable("clfs", "& ...

  3. Docker网络原则入门:EXPOSE,-p,-P,-link

    如果你已经构建了一些多容器的应用程序,那么肯定需要定义一些网络规则来设置容器间的通信.有多种方式可以实现:可以通过--expose参数在运行时暴露端口,或者在Dockerfile里使用EXPOSE指令 ...

  4. Spring —— @Async注解的使用

    参考文档 Spring Boot使用@Async实现异步调用:自定义线程池 Spring Boot使用@Async实现异步调用:ThreadPoolTaskScheduler线程池的优雅关闭

  5. 2018-2019-2 《网络对抗技术》 Exp6 信息搜集与漏洞扫描 20165222

    1.实践目标 掌握信息搜集的最基础技能与常用工具的使用方法. 2.实践内容 (1)各种搜索技巧的应用 https://www.exploit-db.com/收集各种应用软件的漏洞,如tomcat服务器 ...

  6. Cesium的Property机制总结

    前言 Cesium官方教程中有一篇叫<空间数据可视化>(Visualizing Spatial Data).该文文末简单提到了Cesium的Property机制,然后话锋一转,宣告此教程的 ...

  7. Oracle 存储过程—为数传递变量

    oracle 存储过程的基本语法create or replace procedure proc1( p_para1 varchar2, p_para2 out varchar2, p_para3 i ...

  8. java中 int、char、long各占多少字节数

    所谓的占用字节数 就是申请内存的时候所占的空间大小 byte    1字节 最小值是 -128(-2^7):    最大值是 127(2^7-1): boolean    至少1字节 这种类型只作为一 ...

  9. selenium元素input的value值设置【node.js版本】

    driver.executeScript(‘document.getElementById(“id”).value=“value”’); 这个操作就类似于//$("#id").va ...

  10. utf-8的中文是一个字符占几个字节

    utf-8的中文是一个字符占几个字节 英文字母和中文汉字在不同字符集编码下的字节数英文字母:·字节数 : 1;编码:GB2312 字节数 : 1;编码:GBK 字节数 : 1;编码:GB18030 字 ...