首页 > 编程语言 >Dubbo——ExtensionLoader源码解析

Dubbo——ExtensionLoader源码解析

时间:2022-10-18 18:33:20浏览次数:81  
标签:Dubbo name 扩展 class ExtensionLoader 源码 type public

前言

ExtensionLoader,从字面理解,拓展组件加载器,是Dubbo里用来加载器内部SPI(Service Provider Interface)的加载器。在dubbo框架中,每一个SPI接口都对应着自己的ExtensionLoader实例。

 

理解ExtensionLoader的内部机制,能够更得心应手的使用Dubbo的拓展组件,比如Filter,Listener,Protocol,RegistryService,Invoker等等。

简介

dubbo的扩展点框架主要位于这个包下:

org.apache.dubbo.common.extension

大概结构如下:

org.apache.dubbo.common.extension
 |
 |--factory
 |     |--AdaptiveExtensionFactory   #稍后解释
 |     |--SpiExtensionFactory        #稍后解释
 |
 |--support
 |     |--ActivateComparator
 |
 |--Activate  #自动激活加载扩展的注解
 |--Adaptive  #自适应扩展点的注解
 |--ExtensionFactory  #扩展点对象生成工厂接口
 |--ExtensionLoader   #扩展点加载器,扩展点的查找,校验,加载等核心逻辑的实现类
 |--SPI   #扩展点注解

其中最核心的类就是ExtensionLoader,几乎所有特性都在这个类中实现,先来看下他的结构:

 

ExtensionLoader没有提供public的构造方法,但是提供了一个public static的getExtensionLoader,这个方法就是获取ExtensionLoader实例的工厂方法。其public成员方法中有三个比较重要的方法:

  • getActivateExtension :根据条件获取当前扩展可自动激活的实现
  • getExtension : 根据名称获取当前扩展的指定实现
  • getAdaptiveExtension : 获取当前扩展的自适应实现

这三个方法将会是我们重点关注的方法;每一个ExtensionLoader实例仅负责加载特定SPI扩展的实现。因此想要获取某个扩展的实现,首先要获取到该扩展对应的ExtensionLoader实例,下面我们就来看一下获取ExtensionLoader实例的工厂方法getExtensionLoader:

public class ExtensionLoader<T> {

	private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
	
    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 an interface!");
        }
		
		// 只接受使用@SPI注解注释的接口类型
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }
		// 先从静态缓存中获取对应的ExtensionLoader实例
        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;
    }
}

该方法需要一个Class类型的参数,该参数表示希望加载的扩展点类型,该参数必须是接口,且该接口必须被@SPI注解注释,否则拒绝处理。检查通过之后首先会检查ExtensionLoader缓存中是否已经存在该扩展对应的ExtensionLoader,如果有则直接返回,否则创建一个新的ExtensionLoader负责加载该扩展实现,同时将其缓存起来。可以看到对于每一个扩展,dubbo中只会有一个对应的ExtensionLoader实例。

 

接下来看下ExtensionLoader的私有构造函数:

public class ExtensionLoader<T> {

	private final ExtensionFactory objectFactory;

    private ExtensionLoader(Class<?> type) {
        this.type = type;
		// 如果扩展类型是ExtensionFactory,那么则设置为null
		// 这里通过getAdaptiveExtension方法获取一个运行时自适应的扩展类型(每个Extension只能有一个@Adaptive类型的实现,如果没有dubbo会动态生成一个类)
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
}

这里保存了对应的扩展类型,并且设置了一个额外的objectFactory属性,他是一个ExtensionFactory类型,ExtensionFactory主要用于加载扩展的实现。

ExtensionFactory原理

顾名思义,ExtensionFactory是一个扩展点的工厂类。

/**
 * ExtensionFactory
 * 扩展点工厂,加载接口的实现类。这个接口包括Dubbo中的SPI接口和一般类型的接口
 */
@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);

}

从源码可以看出:

  • 1、这是一个SPI接口
  • 2、接口中只有一个方法getExtension 用于获取接口的扩展点实例。
  • 3、 接口中有两个参数,一个是接口的类型,一个扩展实例的名称

我们在看一下ExtensionFactory接口在Dubbo框架中结构层次:

从上图可以看出 ExtensionFactory接口有三个实现类:

  • 1、SpiExtensionFactory 获取dubbo容器中SPI接口扩展点实例工厂,具体实现我们来撸源码:
/**
 * SpiExtensionFactory
 * Dubbo SPI扩展点工厂类,主要功能是从Dubbo容器中获取SPI接口的默认的扩展点
 */
public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
		//1要求type必须是一个接口,并且有@SPI注解。这是dubbo中SPI接口的标准配置
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
			//2调用ExtensionLoader的静态方法获取type接口的ExtensionLoader实例
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
			//3 loader.getSupportedExtensions()返回的是扩展点名称的TreeSet集合
            if (!loader.getSupportedExtensions().isEmpty()) {
				//4使用ExtensionLoader#getAdaptiveExtension()获取默认的实现类
                return loader.getAdaptiveExtension();
            }
        }
		//不满足就直接返回null
        return null;
    }

}

从SpiExtensionFactory 的实现可以看出,底层还是使用的ExtensionLoader#getAdaptiveExtension() 。而参数name在整个过程没有被使用。这里获取SPI接口实例的关键是接口type

  • 2、SpringExtensionFactory 这是Dubbo整合Spring框架时,获取spring的bean容器中的实例的工厂类
/**
 * SpringExtensionFactory
 * dubbo与spring容器的整合
 * 通过SpringExtensionFactory可以获取到spring容器中的扩展类
 */
public class SpringExtensionFactory implements ExtensionFactory {
    private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);

    /**
     *  自动去重的set集合保存spring的上下文对象
     */
    private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();

    public static void addApplicationContext(ApplicationContext context) {
        CONTEXTS.add(context);
        if (context instanceof ConfigurableApplicationContext) {
            ((ConfigurableApplicationContext) context).registerShutdownHook();
        }
    }

    public static void removeApplicationContext(ApplicationContext context) {
        CONTEXTS.remove(context);
    }

    public static Set<ApplicationContext> getContexts() {
        return CONTEXTS;
    }

    // currently for test purpose
    public static void clearContexts() {
        CONTEXTS.clear();
    }

    /**
     * 从spring容器中获取指定class类型和名称的对象
     * @param type object type. 扩展点类型
     * @param name object name. 扩展点名称
     * @param <T> 扩展点class
     * @return 扩展点
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T> T getExtension(Class<T> type, String name) {

        /*
         * SPI should be get from SpiExtensionFactory
         * 如果扩展类时一个接口,并且接口上由@SPI注解,就换回null。
         * 意思是:SPI接口的扩展点实现应该从SpiExtensionFactory中获取
         */	
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            return null;
        }
		
		//遍历Spring的上下文对象ApplicationContext
        for (ApplicationContext context : CONTEXTS) {
			//通过接口类型和接口实现的名称从上下文中获取接口的实例对象,如果有多个实现,默认获取第一个
            T bean = getOptionalBean(context, name, type);
            if (bean != null) {
                return bean;
            }
        }

        //logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
		//Spring容器中没有找到就返回null   
        return null;
    }

}

由实现逻辑可以看出,就是在spring容器的上下文对象作为静态变量存入SpringExtensionFactory中,获取接口实例时就遍历spring的上下文,从中根据接口类型和实现类的名称去查找,有就返回,没有就返回null。

  • 3、AdaptiveExtensionFactory 标注了@Adaptive注解,可见这个是ExtensionFactory默认的实现类。AdaptiveExtensionFactory有个List factories参数
/**
 * AdaptiveExtensionFactory
 * 由于本实现类上有@Adaptive注解,因此它才是ExtensionFactory的默认实现
 * 其本身包含ExtensionFactory的所有实现类
 * 在获取接口实例时,就遍历其他的ExtensionFactory实例。调用他们的getExtension方法 
 */
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    /**
     * 存放SpiExtensionFactory ,SpringExtensionFactory实例
     */
    private final List<ExtensionFactory> factories;

    /**
     * 在构造方法中就加载所有的ExtensionFactory的实例
     */
    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        /*
         * ExtensionLoader.getSupportedExtensions()返回的TreeSet集合,
         * 里面会对ExtensionFactory进行排序,默认排序会使SpiExtensionFactory实例排在前面
         * 这样就会优先从Dubbo的SPI容器中获取扩展点,如果获取不到再从SpringExtensionFactory容器中获取 。
         */
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
		//并使用不可变的list存到内存中
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        /*
		 * 依次遍历各个ExtensionFactory实现的getExtension方法,一旦获取到Extension即返回
		 * 如果遍历完所有的ExtensionFactory实现均无法找到Extension,则返回null
         * 获取扩展点实例,实际是调用SpiExtensionFactory,SpringExtensionFactory等的getExtension 
         */	
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

到这里就能明白了ExtensionFactory接口的工作流程,具体功能实现在SpiExtensionFactory和SpringExtensionFactory中,而AdaptiveExtensionFactory作为默认实现,主要作用是用于管理协调其他的实现。

 

从ExtensionLoader的构造函数中可以看到,如果要加载的扩展点类型是ExtensionFactory是,object字段被设置为null。由于ExtensionLoader的使用范围有限(基本上局限在ExtensionLoader中),因此对他做了特殊对待:在需要使用ExtensionFactory的地方,都是通过对应的自适应实现来代替。

 

默认的ExtensionFactory实现中,AdaptiveExtensionFactotry被@Adaptive注解注释,也就是它就是ExtensionFactory对应的自适应扩展实现(每个扩展点最多只能有一个自适应实现,如果所有实现中没有被@Adaptive注释的,那么dubbo会动态生成一个自适应实现类),也就是说,所有对ExtensionFactory调用的地方,实际上调用的都是AdaptiveExtensionFactory。

 

AdaptiveExtensionFactory会遍历当前系统中所有的ExtensionFactory实现来获取指定的扩展实现,获取到扩展实现或遍历完所有的ExtensionFactory实现。这里调用了ExtensionLoader的getSupportedExtensions方法来获取ExtensionFactory的所有实现,又回到了ExtensionLoader类,下面我们就来分析ExtensionLoader的几个重要的实例方法。

方法调用流程

getExtension

getExtension(name)
    -> createExtension(name) #如果无缓存则创建
        -> getExtensionClasses().get(name) #获取name对应的扩展类型
        -> 实例化扩展类
        -> injectExtension(instance) # 扩展点注入
        -> instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)) #循环遍历所有wrapper实现,实例化wrapper并进行扩展点注入  

getAdaptiveExtension

public T getAdaptiveExtension()
    -> createAdaptiveExtension() #如果无缓存则创建
        -> getAdaptiveExtensionClass().newInstance() #获取AdaptiveExtensionClass
            -> getExtensionClasses() # 加载当前扩展所有实现,看是否有实现被标注为@Adaptive
            -> createAdaptiveExtensionClass() #如果没有实现被标注为@Adaptive,则动态创建一个Adaptive实现类
                -> createAdaptiveExtensionClassCode() #动态生成实现类java代码
                -> compiler.compile(code, classLoader) #动态编译java代码,加载类并实例化
        -> injectExtension(instance)

getActivateExtesion

该方法有多个重载方法,不过最终都是调用了三个参数的那一个重载形式。其代码结构也相对剪短,就不需要在列出概要流程了。

详细代码分析

getAdaptiveExtension

从前面ExtensionLoader的私有构造函数中可以看出,在选择ExtensionFactory的时候,并不是调用getExtension(name)来获取某个具体的实现类,而是调用getAdaptiveExtension来获取一个自适应的实现。那么首先我们就来分析一下getAdaptiveExtension这个方法的实现吧:

public class ExtensionLoader<T> {

    public T getAdaptiveExtension() {
		// 首先判断是否已经有缓存的实例对象
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError != null) {
                throw new IllegalStateException("Failed to create adaptive instance: " +
                        createAdaptiveInstanceError.toString(),
                        createAdaptiveInstanceError);
            }

            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
						// 没有缓存的实例,创建新的AdaptiveExtension实例
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        }

        return (T) instance;
    }
}

首先检查缓存的adaptiveInstance是否存在,如果存在则直接使用,否则的话调用createAdaptiveExtension方法来创建新的adaptiveInstance并且缓存起来。也就是说对于某个扩展点,每次调用ExtensionLoader.getAdaptiveExtension获取到的都是同一个实例。

public class ExtensionLoader<T> {

    private T createAdaptiveExtension() {
        try {
			// 先获取AdaptiveExtensionClass,在获取其实例,最后进行注入处理
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
}

在createAdaptiveExtension方法中,首先通过getAdaptiveExtensionClass方法获取到最终的自适应实现类型,然后实例化一个自适应扩展实现的实例,最后进行扩展点注入操作。先看一个getAdaptiveExtensionClass方法的实现:

public class ExtensionLoader<T> {

    private Class<?> getAdaptiveExtensionClass() {
		// 加载当前Extension的所有实现,如果有@Adaptive类型,则会赋值为cachedAdaptiveClass属性缓存起来
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
		// 没有找到@Adaptive类型实现,则动态创建一个AdaptiveExtensionClass
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
}

他只是简单的调用了getExtensionClasses方法,然后在判adaptiveCalss缓存是否被设置,如果被设置那么直接返回,否则调用createAdaptiveExntesionClass方法动态生成一个自适应实现,关于动态生成自适应实现类然后编译加载并且实例化的过程这里暂时不分析,留到后面在分析吧。这里我们看getExtensionClassses方法:

public class ExtensionLoader<T> {

    private Map<String, Class<?>> getExtensionClasses() {
		// 从缓存中获取已加载的拓展类
        Map<String, Class<?>> classes = cachedClasses.get();
		// 双重检查
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
					// 如果还没有加载Extension的实现,则进行扫描加载,完成后赋值给cachedClasses变量
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }
}

在getExtensionClasses方法中,首先检查缓存的cachedClasses,如果没有再调用loadExtensionClasses方法来加载,加载完成之后就会进行缓存。也就是说对于每个扩展点,其实现的加载只会执行一次。我们看下loadExtensionClasses方法:

public class ExtensionLoader<T> {

    private void cacheDefaultExtensionName() {
		// 获取 SPI 注解,这里的 type 变量是在调用 getExtensionLoader 方法时传入的
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation == null) {
            return;
        }

        String value = defaultAnnotation.value();
        if ((value = value.trim()).length() > 0) {
			// 对 SPI 注解内容进行切分
            String[] names = NAME_SEPARATOR.split(value);
			// 检测 SPI 注解内容是否合法,不合法则抛出异常
			// 每个扩展实现只能配置一个名称
            if (names.length > 1) {
                throw new IllegalStateException("More than 1 default extension name on extension " + type.getName()
                        + ": " + Arrays.toString(names));
            }
            if (names.length == 1) {
				// 设置默认名称,参考 getDefaultExtension 方法
                cachedDefaultName = names[0];
            }
        }
    }

    private Map<String, Class<?>> loadExtensionClasses() {
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();

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

从代码里可以看到通过遍历LoadingStrategy接口的子类,加载指定文件夹下的配置文件

  • DubboInternalLoadingStrategy
public class DubboInternalLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/dubbo/internal/";
    }

    @Override
    public int getPriority() {
        return MAX_PRIORITY;
    }
}
  • DubboLoadingStrategy
public class DubboLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/dubbo/";
    }

    @Override
    public boolean overridden() {
        return true;
    }

    @Override
    public int getPriority() {
        return NORMAL_PRIORITY;
    }


}
  • ServicesLoadingStrategy
public class ServicesLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/services/";
    }

    @Override
    public boolean overridden() {
        return true;
    }

    @Override
    public int getPriority() {
        return MIN_PRIORITY;
    }

}

从这里的代码中又可以看到,具体的扩展实现类型,是通过调用loadDirectory方法来加载,分别从一下三个地方加载:

  • META-INF/dubbo/internal/
  • META-INF/dubbo/
  • META-INF/services/
public class ExtensionLoader<T> {

    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        loadDirectory(extensionClasses, dir, type, false, false);
    }

    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
                               boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
        // fileName = 文件夹路径 + type 全限定名 
		String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls = null;
            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();
					// 加载资源
                    loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }
	
}

loadResource(),用于读取和解析配置文件,并通过反射加载类

public class ExtensionLoader<T> {

    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;
                            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 方法对类进行缓存
                                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() 方法对 @Activate 的扫描,其中会将包含 @Activate 注解的实现类缓存到 cachedActivates 这个实例字段(Map<String, Object>类型,Key为扩展名,Value为 @Activate 注解):

public class ExtensionLoader<T> {

    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.");
        }
        // 检测目标类上是否有 Adaptive 注解
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            // 处理@Adaptive注解 
            cacheAdaptiveClass(clazz, overridden);
        } else if (isWrapperClass(clazz)) {// 检测 clazz 是否是 Wrapper 类型
            // 处理Wrapper类 
            cacheWrapperClass(clazz);
        } else {// 处理真正的扩展实现类 
            // 扩展实现类必须有无参构造函数 
            clazz.getConstructor();
            if (StringUtils.isEmpty(name)) {
                // 如果 name 为空,则尝试从 Extension 注解中获取 name,或使用小写的类名作为 name
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }
            // 切分 name
            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                // 将包含@Activate注解的实现类缓存到cachedActivates集合中 
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    // 在cachedNames集合中缓存实现类->扩展名的映射 
                    cacheName(clazz, n);
                    // 在cachedClasses集合中缓存扩展名->实现类的映射 
                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                }
            }
        }
    }
}

上面的方法较多,理一下逻辑:

  • 1、getExtensionClasses():先检查缓存,若缓存未命中,则通过 synchronized 加锁。加锁后再次检查缓存,并判断是否为空。此时如果 classes 仍为 null,则通过 loadExtensionClasses 加载拓展类。

  • 2、loadExtensionClasses():对 SPI 注解的接口进行解析,而后调用 loadDirectory 方法加载指定文件夹配置文件。

  • 3、loadDirectory():方法先通过 classLoader 获取所有资源链接,然后再通过 loadResource 方法加载资源。

  • 4、loadResource():用于读取和解析配置文件,并通过反射加载类,最后调用 loadClass 方法进行其他操作。loadClass 方法用于主要用于操作缓存。

当loadExtensionClasses方法执行完成之后,cachedDefaultName 变量被赋值:

  • cachedDefaultName : 当前扩展点的默认实现名称

当getExtensionClasses方法执行完成之后,除了上述变量被赋值之外,还有以下变量被赋值:

  • cachedClasses : 扩展点实现名称对应的实现类(一个实现类可能有多个名称)

其实也就是说,在调用了getExtensionClasses方法之后,当前扩展点对应的实现类的一些信息就已经加载进来了并且被缓存了。后面的许多操作都可以直接通过这些缓存数据来进行处理了。

 

回到createAdaptiveExtension方法,他调用了getExtesionClasses方法加载扩展点实现信息完成之后,就可以直接通过判断cachedAdaptiveClass缓存字段是否被赋值盘确定当前扩展点是否有默认的AdaptiveExtension实现。如果没有,那么就调用createAdaptiveExtensionClass方法来动态生成一个。在dubbo的扩展点框架中大量的使用了缓存技术。

 

创建自适应扩展点实现类型和实例化就已经完成了,下面就来看下扩展点自动注入的实现injectExtension,也被称为Dubbo中的IOC。

Dubbo中的IOC

private T injectExtension(T instance) {

    if (objectFactory == null) {
        // 检测objectFactory字段 
        return instance;
    }

    try {
        for (Method method : instance.getClass().getMethods()) {
            // 如果不是setter方法,忽略该方法(略) 
            if (!isSetter(method)) {
                continue;
            }
            /**
             * Check {@link DisableInject} to see if we need auto injection for this property
             */
            // 如果方法上明确标注了@DisableInject注解,忽略该方法
            if (method.getAnnotation(DisableInject.class) != null) {
                continue;
            }
            // 根据setter方法的参数,确定扩展接口 
            Class<?> pt = method.getParameterTypes()[0];
            
            // 如果参数为简单类型,忽略该setter方法
            if (ReflectUtils.isPrimitives(pt)) {
                continue;
            }

            try {
                // 根据setter方法的名称确定属性名称
                String property = getSetterProperty(method);
                
                // 加载并实例化扩展实现类
                Object object = objectFactory.getExtension(pt, property);
                if (object != null) {
                    // 调用setter方法进行装配
                    method.invoke(instance, object);
                }
            } catch (Exception e) {
                logger.error("Failed to inject via method " + method.getName()
                        + " of interface " + type.getName() + ": " + e.getMessage(), e);
            }

        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

在上面代码中,objectFactory 变量的类型为 AdaptiveExtensionFactory,AdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory。Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactory 和 SpringExtensionFactory。前者用于创建自适应的拓展,后者是用于从 Spring 的 IOC 容器中获取所需的拓展。这就是我们常说的Dubbo为什么能够与Spring无缝连接,因为Dubbo底层就是依赖Spring的,对于Spring的IOC容器可直接拿来用。

 

这里可以看到,扩展点自动注入的一句就是根据setter方法对应的参数类型和property名称从ExtensionFactory中查询,如果有返回扩展点实例,那么就进行注入操作。到这里getAdaptiveExtension方法就分析完毕了。

getExtension

这个方法的主要作用是用来获取ExtensionLoader实例代表的扩展的指定实现。已扩展实现的名字作为参数,结合前面学习getAdaptiveExtension的代码,我们可以推测,这方法中也使用了在调用getExtensionClasses方法的时候收集并缓存的数据,其中涉及到名字和具体实现类型对应关系的缓存属性是cachedClasses。

public class ExtensionLoader<T> {

    public T getExtension(String name, boolean wrap) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
		// 获取默认的拓展实现类
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
		// Holder,顾名思义,用于持有目标对象
		// getOrCreateHolder()方法中封装了查找cachedInstances缓存的逻辑 
        final Holder<Object> holder = getOrCreateHolder(name);
        Object instance = holder.get();
		// 双重检查
        if (instance == null) {
            synchronized (holder) {// double-check防止并发问题 
                instance = holder.get();
                if (instance == null) {
					// 创建拓展实例
					// 根据扩展名从SPI配置文件中查找对应的扩展实现类 
                    instance = createExtension(name, wrap);
					// 设置实例到 holder 中
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }
	
}

接着看createExtension方法的实现:

public class ExtensionLoader<T> {

    private T createExtension(String name, boolean wrap) {
		// getExtensionClass内部使用cachedClasses缓存
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
			// 从已创建Extension实例缓存中获取
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
			// 属性注入
            injectExtension(instance);


            if (wrap) {

                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
					 // Wrapper类型进行包装,层层包裹
                    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);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }
	
}

从代码中可以看到,内部调用了getExtensionClasses方法来获取当前扩展的所有实现,而getExtensionClassse方法会在第一次被调用的时候将结果缓存到cachedClasses变量中,后面的调用就直接从缓存变量中获取了。这里还可以看到一个缓存EXTENSION_INSTANCES,这个缓存是ExtensionLoader的静态成员,也就是全局缓存,存放着所有的扩展点实现类型与其对应的已经实例化的实例对象(是所有扩展点,不是某一个扩展点),也就是说所有的扩展点实现在dubbo中最多都只会有一个实例。

 

拿到扩展点实现类型对应的实例之后,调用了injectExtension方法对该实例进行扩展点注入,紧接着就是遍历该扩展点接口的所有Wrapper来对真正的扩展点实例进行Wrap操作,都是对通过将上一次的结果作为下一个Wrapper的构造函数参数传递进去实例化一个Wrapper对象,最后总返回回去的是Wrapper类型的实例而不是具体实现类的实例。

getActivateExtension

getActivateExtension方法主要获取当前扩展的所有可自动激活的实现。可根据入参(values)调整指定实现的顺序,在这个方法里面也使用到getExtensionClasses方法中收集的缓存数据。

public class ExtensionLoader<T> {

    public List<T> getActivateExtension(URL url, String[] values, String group) {
        List<T> activateExtensions = new ArrayList<>();
        // values配置就是扩展名 
        List<String> names = values == null ? new ArrayList<>(0) : asList(values);
		
		// 如果未配置"-default",则加载所有Activates扩展(names指定的扩展)
        if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
            // 触发cachedActivates等缓存字段的加载 
			// 加载当前Extension所有实现,会获取到当前Extension中所有@Active实现,赋值给cachedActivates变量
            getExtensionClasses();
            for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
                // 扩展名 
                String name = entry.getKey();
                // @Activate注解 
                Object activate = entry.getValue();

                String[] activateGroup, activateValue;

                // @Activate注解中的配置 
                if (activate instanceof Activate) {
                    activateGroup = ((Activate) activate).group();
                    activateValue = ((Activate) activate).value();
                    
                    //兼容老版Activate注解
                } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
                    activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
                    activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
                } else {
                    continue;
                }
                // 匹配group 
                if (isMatchGroup(group, activateGroup)
                        // 没有出现在values配置中的,即为默认激活的扩展实现 
                        && !names.contains(name)
                        // 通过"-"明确指定不激活该扩展实现 
                        && !names.contains(REMOVE_VALUE_PREFIX + name)
                        // 检测URL中是否出现了指定的Key 
                        && isActive(activateValue, url)) {
                     // 加载扩展实现的实例对象,这些都是激活的
                    activateExtensions.add(getExtension(name));
                }
            }
            // 排序
            activateExtensions.sort(ActivateComparator.COMPARATOR);
        }
        List<T> loadedExtensions = new ArrayList<>();
        for (int i = 0; i < names.size(); i++) {
            String name = names.get(i);
            // 通过"-"开头的配置明确指定不激活的扩展实现,直接就忽略了 
            if (!name.startsWith(REMOVE_VALUE_PREFIX)
                    && !names.contains(REMOVE_VALUE_PREFIX + name)) {
                if (DEFAULT_KEY.equals(name)) {
                    if (!loadedExtensions.isEmpty()) {
                        // 按照顺序,将自定义的扩展添加到默认扩展集合前面 
                        activateExtensions.addAll(0, loadedExtensions);
                        loadedExtensions.clear();
                    }
                } else {
                    loadedExtensions.add(getExtension(name));
                }
            }
        }
        if (!loadedExtensions.isEmpty()) {
            // 按照顺序,将自定义的扩展添加到默认扩展集合后面 
            activateExtensions.addAll(loadedExtensions);
        }
        return activateExtensions;
    }
}

总结

基本上将dubbo的扩展点加载机制学习了一遍,有几点可能需要注意的地方:

  • 1、每个ExtensionLoader实例只负责加载一个特定扩展点实现。
  • 2、每个扩展点对应最多只有一个ExtensionLoader实例。
  • 3、对于每个扩展点实现,最多只会有一个实例。
  • 4、一个扩展点实现可以对应多个名称(逗号分隔)。
  • 5、对于需要等到运行时才能决定使用哪一个具体实现的扩展点,应获取其自使用扩展点实现(AdaptiveExtension)
  • 6、@Adaptive注解要么注释在扩展点@SPI的方法上,要么注释在其实现类的类定义上。
  • 7、如果@Adaptive注解注释在@SPI接口的方法上,那么原则上该接口所有方法都应该加@Adaptive注解(自动生成的实现中默认为注解的方法抛异常)
  • 8、每个扩展点最多只能有一个被AdaptiveExtension。
  • 9、每个扩展点可以有多个可自动激活的扩展点实现(使用@Activate注解)
  • 10、由于每个扩展点实现最多只有一个实例,因此扩展点实现应保证线程安全
  • 11、如果扩展点有多个Wrapper,那么最终其执行的顺序不确定(内部使用ConcurrentHashSet存储)

 

参考: https://blog.csdn.net/jdluojing/article/details/44947221

https://blog.csdn.net/zhaodongchao1992/article/details/103374160

标签:Dubbo,name,扩展,class,ExtensionLoader,源码,type,public
From: https://blog.51cto.com/u_14014612/5767953

相关文章

  • Dubbo——时间轮(Time Wheel)算法应用
    定时任务Netty、Quartz、Kafka以及Linux都有定时任务功能。 JDK自带的java.util.Timer和DelayedQueue可实现简单的定时任务,底层用的是堆,存取复杂度都是O(nlog(......
  • dubbo 源码解析----- 服务引用
    转自:https://blog.csdn.net/beichen8641/article/details/104815163 在Dubbo中,我们可以通过两种方式引用远程服务。第一种是使用服务直连的方式引用服务,第二种方式是......
  • CVPR2021:IoU优化——在Anchor-Free中提升目标检测精度(附源码)
    计算机视觉研究院专栏作者:Edison_G目前的anchor-free目标检测器非常简单和有效,但缺乏精确的标签分配方法,这限制了它们与经典的基于Anchor的模型竞争的潜力1 简要目前的anch......
  • Dubbo——Dubbo中的URL统一资源模型与Dubbo协议
    一、URL简介在互联网领域,每个信息资源都有统一的且在网上唯一的地址,该地址就叫URL(UniformResourceLocator,统一资源定位符),它是互联网的统一资源定位标志,也就是指网络地址......
  • 计算机视觉研究院:近期目标检测框架爱回顾&总结(附论文、源码、链接)
    计算机视觉研究院专栏作者:Edison_G最近我们“计算机视觉研究院”主要推送了目标检测干货及Yolo系列的高质量文章及实践,今天给大家总结一下!目标检测是现在最热门的研究课题,也......
  • Dubbo——Dubbo SPI解析(上)
    前言Dubbo为了更好地达到OCP原则(即“对扩展开放,对修改封闭”的原则),采用了“微内核+插件”的架构。那什么是微内核架构呢?微内核架构也被称为插件化架构(Plug-inArchitect......
  • vue源码分析-diff算法核心原理
    这一节,依然是深入剖析Vue源码系列,上几节内容介绍了VirtualDOM是Vue在渲染机制上做的优化,而渲染的核心在于数据变化时,如何高效的更新节点,这就是diff算法。由于源码中关于d......
  • vue源码分析-插槽原理
    Vue组件的另一个重要概念是插槽,它允许你以一种不同于严格的父子关系的方式组合组件。插槽为你提供了一个将内容放置到新位置或使组件更通用的出口。这一节将围绕官网对插......
  • 交流群里的两个实例--直接放源码了
    导读本文主要介绍交流群里的两个实例,直接放源码。(公众号:OpenCV与AI深度学习)实例一  要求:识别下图中加粗的文本内容。  实现步骤:        【1】闭运算减少......
  • python实现超级玛丽小游戏(动图演示+源码分享)
    下面给大家带来python实现超级玛丽小游戏的源码分享 效果演示:  1.基础设置(tools部分)这个部分设置马里奥以及游戏中蘑菇等怪的的移动设置。importosimpor......