Soul 学习笔记之 ExtensionLoader(十五)

总系列目录地址

ExtentionLoader

SPI 全称 Service Provider Interface,实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。
使用 SPI 方式可以让具体实现类在程序外部单独编程,实现可插拔,达到解耦的目的

Soud 中的 ExtentionLoader 起源于 apache dubbo, 是 SPI 方式的扩展使用。

使用方式

LoadBalanceUtils

使用不同的负载均衡策略
LoadBalanceUtils.selector

public static DivideUpstream selector(final List<DivideUpstream> upstreamList, final String algorithm, final String ip) {
	LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getJoin(algorithm);
    return loadBalance.select(upstreamList, ip);
}

ExtensionLoader.getExtensionLoader(LoadBalance.class)

  1. Map<Class<?>, ExtensionLoader<?>> LOADERS 中读取 key 为 LoadBalance.class 的 ExtensionLoader 实现。

  2. Map<Class<?>, ExtensionLoader<?>> LOADERS 有一个根节点 key 为 ExtensionFactory.class

  3. Holder<Map<String, Class<?>>> cachedClasses 中保存所有 META-INF/soul/ 中的 class 信息。

  4. 加载外部类的具体代码 返回的类保存到 cachedClasses 中。

    private Map<String, Class<?>> loadExtensionClass() {
    	// SPI 注解加在接口类中, 如: interface LoadBalance 
        SPI annotation = clazz.getAnnotation(SPI.class);
        ...
        Map<String, Class<?>> classes = new HashMap<>(16);
        loadDirectory(classes);
        return classes;
    }
    
  5. 通过 META-INF/soul/ 加载类
    meta-inf

    private void loadDirectory(final Map<String, Class<?>> classes) {
    	// clazz.getName = org.dromara.soul.plugin.divide.balance.LoadBalance
        String fileName = SOUL_DIRECTORY + clazz.getName();
        try {
            ClassLoader classLoader = ExtensionLoader.class.getClassLoader();
            // 加载文件里所有的元素
            // random=org.dromara.soul.plugin.divide.balance.spi.RandomLoadBalance
    		// roundRobin=org.dromara.soul.plugin.divide.balance.spi.RoundRobinLoadBalance
    		// hash=org.dromara.soul.plugin.divide.balance.spi.HashLoadBalance
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources(fileName)
                    : ClassLoader.getSystemResources(fileName);
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    loadResources(classes, url);
                }
            }
        }
    }
    
  6. 通过 METE-INFO 找到的实现类需要加上 @Join 才会被加载,否则会报错。

    private void loadClass(final Map<String, Class<?>> classes,final String name, final String classPath) {
    	....
    	Join annotation = subClass.getAnnotation(Join.class);
        if (annotation == null) {
    	        throw new IllegalStateException("load extension resources error," + subClass + " with Join annotation");
        }
    }
    

getJoin(algorithm)

public T getJoin(final String name) {
   	 ...
   	 // cachedInstances 在初始化时已经
     Holder<Object> objectHolder = cachedInstances.get(name);
     if (objectHolder == null) {
         cachedInstances.putIfAbsent(name, new Holder<>());
         objectHolder = cachedInstances.get(name);
     }
     Object value = objectHolder.getValue();
     if (value == null) {
         synchronized (cachedInstances) {
             value = objectHolder.getValue();
             if (value == null) {
                 value = createExtension(name);
                 objectHolder.setValue(value);
             }
         }
     }
     return (T) value;
 }
 private T createExtension(final String name) {
    ... 
   	// 已经使用过 getJoin 的会有一个缓存
    Object o = joinInstances.get(aClass);
    if (o == null) {
        try {
            joinInstances.putIfAbsent(aClass, aClass.newInstance());
            o = joinInstances.get(aClass);
        }...
    }
    return (T) o;
}

具体执行时序图

  1. getExtensionLoader 部分时序图
Class LoadBalanceUtils ExtensionLoader getExtensionClasses loadExtensionClass loadDirectory loadResources loadClass selector getExtensionLoader 从 Map:LOADERS 中获取ExtensionLoader 不存在则创建 从 cachedClasses 获取 Map 找到注解 SPI 的接口类,并新建 Map 获取 META-INF/soul 下的文件 读取 properties 的内容 找到注解 Join 的实现类, 放到新创建的 Map void void void Map Map ExtensionLoader doSomeThing Class LoadBalanceUtils ExtensionLoader getExtensionClasses loadExtensionClass loadDirectory loadResources loadClass ExtentionLoader getExtensionLoader
  1. getJoin 部分时序图
Class LoadBalanceUtils ExtensionLoader createExtension getExtensionClasses selector getJoin 从 cachedInstances 中获取ExtensionLoader 不存在,则新建 在 cachedClasses 查找是否存在 还没被加载过的类,会执行 loadExtensionClass Map.get 在 joinInstances 中查找,不存在则 newInstance T T doSomeThing Class LoadBalanceUtils ExtensionLoader createExtension getExtensionClasses ExtentionLoader getJoin

总结

实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制
接口类必须加上 @SPI, 实现类必须加上 @Join
ExtensionLoader 在多个 RPC 框架都有实现, 如: dubbo, sofaRpc
sofaRpc 有 @Extensible 和 @Extension, 对应 sofa @SPI 和 @Join

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值