Dubbo-SPI

1、扩展点机制

官方文档

什么是SPI

  • SPI(Service Provider Interface)实际是一种服务提供者的发现机制,通过规范约束加载服务实现类实现接口与实现的扩展与解耦。

Java SPI

  • Java SPI是Java内置的一套SPI机制,通过ServiceLoader加载META-INF/services目录下创建接口的全限定名文件,文件内容为接口实现类的全限定名,以换行符分隔
  • Java SPI的缺点是会一次性加载并实例化所有的扩展点实现,这无疑会造成资源浪费的情况。

Dubbo SPI

  • Dubbo SPI对比Java SPI做了改进和优化,添加了IOC和AOP的支持,优化的异常信息,扩展类使用时初始化,
  • Dubbo SPI加载META-INF/dub目录下的配置文件,文件名为接口全限定名,文件内容为key-value键值对,value为接口实现类的全限定名,key为获取value的关键字。接口必须添加@SPI注解,使用ExtensionLoader加载扩展类。

2、扩展点特性

自动包装

  • ExtensionLoader 在加载扩展时,如果发现这个扩展类包含其他扩展点作为构造函数的参数 , 则这个扩展类就会被认为是 Wrapper类(包装类)
  • 包装类与构造器参数实现了共同的扩展接口,扩展点添加增强功能。
  • 例如ProtocolFilterWrapper包装类,

自动装配

  • Dubbo通过setter方法判断成员是否为其他扩展类,如果是则通过ExtensionLoader注入扩展类。
  • 一个接口会有多个实现类,那么自动装配的时候如何判断应该注入哪个实现类?答案是自适应

自适应

  • 扩展点方法需要URL参数,Dubbo提供了@Adaptive注解,通过@Adaptive注解和URL参数判断最终选取的扩展实现类。
  • @Adaptive可以用在类和方法上
标记点 作用
方法 通过参数动态获得实现类,Dubbo会默认生成一个Adaptive的代理类,在代理类中通过URL参数获取具体实现类,然后调用目标方法。
当@Adaptive作用在实现类上时,Dubbo就不会再自动生成Adaptive的代理类,而是使用@Adaptive标记的实现类作为,可以说是从动态代理改为了静态代理,所以只能有一个实现类使用@Adaptive注解。

自动激活

  • Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker等扩展点,可以有多个实现类,使用@Activate注解可以针对不同场景使用不同的实现类。
  • @Activate可以标记在类、和方法上。主要使用在有多个扩展点实现、需要根据不同条件被激活的场景中。

注解

@SPI

  • @SPI注解用在接口上,主要作用是标记这个接口是Dubbo的SPI接口
  • @SPI有一个默认值value,可以指定接口的默认实现。

@Adaptive

修饰接口方法,实现类

  • 方法:Dubbo默认会生成一个动态代理类,参数包含URL对象,可以根据URL参数动态获取实现类。
  • 类:当再实现类使用此注解时,Dubbo不会生成动态代理类,而是使用此类作为静态代理。扩展点可以有多个实现类,但至多只能有一个类使用@Adaptive注解。

@Activate

  • 一般用在接口上,为扩展点赋予条件属性,如分组、排序、url的key等信息,可以在条件的不同在不同的场景中激活扩展功能。

3、ExtensionLoader

T getExtension(String name)

主要分为2步

  • 根据name获取class
  • 根据class获取实例信息

这2步都使用了Map缓存。先从缓存中获取信息,没有则加载然后初始化并放入缓存。

/** 方法大致分为两步,1、获取class的信息,2、获取class的实例信息*/
public T getExtension(String name) {
	。。。 // 省略代码
        // 1、获取或创建class的持有者
	final Holder<Object> holder = getOrCreateHolder(name);
	Object instance = holder.get();
	if (instance == null) {
		synchronized (holder) {
			instance = holder.get();
			if (instance == null) {
                                // 2、创建class的实例
				instance = createExtension(name);
				holder.set(instance);
			}
		}
	}
	return (T) instance;
}
/** 从缓存cachedInstances中获取,如果没有则创建一个空的holder*/
private Holder<Object> getOrCreateHolder(String name) {
	Holder<Object> holder = cachedInstances.get(name);
	if (holder == null) {
		cachedInstances.putIfAbsent(name, new Holder<>());
		holder = cachedInstances.get(name);
	}
	return holder;
}
/** 
1、getExtensionClasses会在cachedClasses缓存中获取class信息
2、如果缓存中不存在,则去META-INF/dubbo、META-INF/dubbo/internal、META-INF/services这三个目录下扫描文件
并将文件信息缓存至cachedClasses和cachedNames,然后根据参数name得到class信息
3、完成了配置文件加载后,在EXTENSION_INSTANCES中查询根据class查询是否存在class的实例缓存,如果没有则根据class创建实例并插入到EXTENSION_INSTANCES缓存中
上面3步完成了class的实例获取,接着需要对实例中的成员信息进行注入
4、注入的条件是必须是setter方法,且参数只能有一个
5、然后去SpiExtensionFactory和SpringExtensionFactory查找符合class与name的实例,如果存在该实例,则通过反射注入。
在2步,加载配置文件的类信息时,如果类的构造方法的参数是当前类的类型,则会将类信息缓存至cachedWrapperClasses
6、类初始化以后,需要自动装配,循环cachedWrapperClasses,根据上面的实例信息初始化其他类实例。再对实例信息进行成员变量注入
7、对包装类进行初始化并注入成员变量
*/
private T createExtension(String name) {
        // 根据名称获取class对象
        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);
            }
            // 成员信息注入
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            // 包装类实例初始化并注入成员变量
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                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 + ") couldn't be instantiated: " + t.getMessage(), t);
        }
 }

T getAdaptiveExtension()

获取适配扩展类,@Adaptive修饰接口类无效,修饰实现类方法无效,可以修饰接口的方法和实现类。

/** 获取扩展点适配器类,如果缓存不存在,则加载配置文件并初始化类信息*/
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("Failed to create adaptive instance: " + t.toString(), t);
					}
				}
			}
		} else {
			throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
		}
	}
	return (T) instance;
}

List getActivateExtension(URL url, String[] values, String group)

/** 方法包含两个功能,1、先获取加了@Activate注解的实现类信息,然后初始化实例并返回,2、根据url参数解析出name,然后根据name获取实例信息*/
public List<T> getActivateExtension(URL url, String[] values, String group) {
    List<T> exts = new ArrayList<>();
    List<String> names = values == null ? new ArrayList<>(0) : Arrays.asList(values);
    // 获取加了@Activate注解的实现类信息,然后初始化实例
    if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
        getExtensionClasses();
        for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
            String name = entry.getKey();
            Object activate = entry.getValue();

            String[] activateGroup, activateValue;

            if (activate instanceof Activate) {
                activateGroup = ((Activate) activate).group();
                activateValue = ((Activate) activate).value();
            } 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;
            }
            if (isMatchGroup(group, activateGroup)) {
                T ext = getExtension(name);
                if (!names.contains(name)
                        && !names.contains(REMOVE_VALUE_PREFIX + name)
                        && isActive(activateValue, url)) {
                    exts.add(ext);
                }
            }
        }
        exts.sort(ActivateComparator.COMPARATOR);
    }
    // 根据url参数解析出name,然后根据name获取实例信息
    List<T> usrs = 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 (!usrs.isEmpty()) {
                    exts.addAll(0, usrs);
                    usrs.clear();
                }
            } else {
                T ext = getExtension(name);
                usrs.add(ext);
            }
        }
    }
    if (!usrs.isEmpty()) {
        exts.addAll(usrs);
    }
    return exts;
}

posted @ 2021-02-19 16:36  Simple°  阅读(144)  评论(0编辑  收藏  举报