自定义头部 -->

Dubbo的SPI中的IOC和API

Dubbo的SPI中的IOC和API

SPI(Service Provider Interface)是服务发现机制, JDK也有SPI机制, 但Dubbo的SPI是对其的增强和扩展, 官方是这么说的:

  • JDK 标准的 SPI 会一次性实例化扩展点所有实现, 如果有扩展实现初始化很耗时, 但如果没用上也加载, 会很浪费资源.
  • 如果扩展点加载失败, 连扩展点的名称都拿不到了.比如: JDK 标准的 ScriptEngine, 通过 getName() 获取脚本类型的名称, 但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在, 导致 RubyScriptEngine 类加载失败, 这个失败原因被吃掉了, 和 ruby 对应不起来, 当用户执行 ruby 脚本时, 会报不支持 ruby, 而不是真正失败的原因.
  • 增加了对扩展点 IoC 和 AOP 的支持, 一个扩展点可以直接 setter 注入其它扩展点.

看起来花里胡哨的,我是这么理解的:

  • jdk SPI通过接口类名获取所有实现, 但是Duboo SPI可以根据接口类名和key值获取具体一个实现
  • 对扩展类实例的属性进行依赖注入, 即IOC
  • 采用装饰器模式实现AOP功能

Dubbo SPI实战

  • e.g.其实用的时候就就一行代码:
Barm demo = ExtensionLoader.getExtensionLoader(Barm.class).getExtension("demo");
  • 首先要定义一个SPI接口, 当然你也可以使用Dubbo的.

Pre: 一定要 被@SPI注解修饰 @SPI("demo") 表示默认使用名为demo的服务

package com.barm.order.server;
import org.apache.dubbo.common.extension.SPI;
@SPI
public interface Barm {
    String info();
}
  • 定义接口服务
package com.barm.order.server;

import com.barm.order.server.Barm;

public class DemoBarm implements Barm {
    @Override
    public String info() {
        return "helloWorld";
    }
}
  • 配置文件存放在项目resource目录下的的下面三种路径的其中一个.
  META-INF/services/
  META-INF/dubbo/
  META-INF/dubbo/internal/
  • 配置文件名为实现的SPI接口的全限定名:com.barm.order.server. BarmmetaInterface
  • 配置文件设计成KV键值对的形式 getExtension("demo")
demo=com.barm.order.server.DemoBarm

找个地方执行以下

run

读源码

如果源码比较熟悉的话这段可以跳过

的基本属性

 /**
  * 拓展加载器集合
  * key: 拓展接口
  */
 private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
 /**
  * 拓展实现类集合
  * key: 拓展实现类
  * value: 拓展对象.
  */
 private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
 
 
 /**
  * 拓展接口.
  * 例如,Protocol
  */
 private final Class<?> type;
 /**
  * 对象工厂
  * 用于调用 {@link #injectExtension(Object)} 方法,向拓展对象注入依赖属性.
  * 例如,StubProxyFactoryWrapper 中有 `Protocol protocol` 属性.
  */
 private final ExtensionFactory objectFactory;
 /**
  * 缓存的拓展名与拓展类的映射.
  * 和 {@link #cachedClasses} 的 KV 对调.
  * 通过 {@link #loadExtensionClasses} 加载
  */
 private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
 /**
  * 缓存的拓展实现类集合.
  *
  * 不包含如下两种类型: 
  *  1. 自适应拓展实现类.例如 AdaptiveExtensionFactory
  *  2. 带唯一参数为拓展接口的构造方法的实现类.例如,ProtocolFilterWrapper
  *   拓展 Wrapper 实现类,会添加到 {@link #cachedWrapperClasses} 中
  *
  * 通过 {@link #loadExtensionClasses} 加载
  */
 private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
 
 /**
  * 拓展名与 @Activate 的映射
  *
  * 例如,AccessLogFilter.
  * 用于 {@link #getActivateExtension(URL, String)}
  */
 private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
 /**
  * 缓存的拓展对象集合
  *
  * key: 拓展名
  * value: 拓展对象
  *
  * 例如,Protocol 拓展
  *      key: dubbo value: DubboProtocol
  *      key: injvm value: InjvmProtocol
  * 通过 {@link #loadExtensionClasses} 加载
  */
 private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
 /**
  * 缓存的自适应( Adaptive )拓展对象
  */
 private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
 /**
  * 缓存的自适应拓展对象的类
  * {@link #getAdaptiveExtensionClass()}
  */
 private volatile Class<?> cachedAdaptiveClass = null;
 /**
  * 缓存的默认拓展名
  * 通过 {@link SPI} 注解获得
  */
 private String cachedDefaultName;
 /**
  * 创建 {@link #cachedAdaptiveInstance} 时发生的异常.
  * 发生异常后,不再创建,参见 {@link #createAdaptiveExtension()}
  */
 private volatile Throwable createAdaptiveInstanceError;
 
 /**
  * 拓展 Wrapper 实现类集合
  *
  * 带唯一参数为拓展接口的构造方法的实现类
  * 通过 {@link #loadExtensionClasses} 加载
  */
 private Set<Class<?>> cachedWrapperClasses;

getExtensionLoader 通过name(key)来获取具体的扩展

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

getExtension 先查缓存, 如果没有则双检锁方式创建实例

public T getExtension(String name) {
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    if ("true".equals(name)) {
        // 默认拓展实现类
        return getDefaultExtension();
    }
    // 获取持有目标对象
    Holder<Object> holder = getOrCreateHolder(name);
    Object instance = holder.get();
    // 双检锁
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                // 创建实例
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

createExtension

核心方法 当中包含IOC和AOP的设计

private T createExtension(String name) {
    // 1.加载配置文件所有拓展类, 得到配置名->拓展类的map, 从map中获取到拓展类
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            // 2.通过反射创建实例
            // EXTENSION_INSTANCES这个map是配置名-拓展类实例的map
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        // 3.注入依赖, 即IOC
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
            // 4.循环创建Wrapper实例
            for (Class<?> wrapperClass : wrapperClasses) {
                // 通过反射创建Wrapper实例
                // 向Wrapper实例注入依赖, 最后赋值给instance
                // 自动包装实现类似aop功能
                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);
    }
}

getExtensionClasses 找出所有拓展类, 返回一个配置名-拓展类的map

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 解析SPI注解, 指定目录的配置文件

private Map<String, Class<?>> loadExtensionClasses() {
    // 获取SPI注解, 检查合法等
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if(defaultAnnotation != null) {
        String value = defaultAnnotation.value();
        if(value != null && (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];
        }
    }
    
    Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    // META-INF/dubbo/internal/目录
    loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    // META-INF/dubbo/目录
    loadFile(extensionClasses, DUBBO_DIRECTORY);
    // META-INF/services/目录
    loadFile(extensionClasses, SERVICES_DIRECTORY);
    return extensionClasses;
}

Dubbo IOC

Dubbo IOC是通过反射setter方法注入依赖的, 先过滤出含有setter特征的方法.然后通过objectFactory获取依赖对象进行注入

注意看 createExtension > injectExtension

private T injectExtension(T instance) {
    try {
        if (objectFactory != null) {
            for (Method method : instance.getClass().getMethods()) {
                // 如果方法以set开头 && 只有一个参数 && 方法是public级别的
                if (method.getName().startsWith("set")
                        && method.getParameterTypes().length == 1
                        && Modifier.isPublic(method.getModifiers())) {
                    // 获取setter方法参数类型
                    Class<?> pt = method.getParameterTypes()[0];
                    try {
                        String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                        // 关键, 从objectFactory里拿出依赖对象
                        Object object = objectFactory.getExtension(pt, property);
                        if (object != null) {
                            // 利用反射进行注入
                            method.invoke(instance, object);
                        }
                    } catch (Exception e) {
                        logger.error("fail to inject via method " + method.getName()
                                + " of interface " + type.getName() + ": " + e.getMessage(), e);
                    }
                }
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

objectFactory: ExtensionFactory类型的, 自身也是一个扩展点

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

objectFactory

getExtensionfactories有两个, 分别是SpiExtensionFactory和SpringExtensionFactory

8ah5OH.png

  • 对于SpringExtensionFactory就是从工厂里根据beanName拿到Spring bean来注入

  • 对于SpiExtensionFactory就是根据传入Class获取自适应拓展类

Dubbo AOP

Dubbo AOP 是通过装饰者模式,使用包装类包装原始的扩展点实例.在扩展点实现前后插入其他逻辑,实现AOP功能.其实就是wrapper代理一下. 感觉比SpringAOP的灵活性要差一点.

e.g.

filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper

找个wrapper对比这看一下就明白了.

AOP e.g.

获得自适应的拓展对象

参考 Dubbo 开发指南 >扩展点加载> 扩展点自适应

这段代码应该也比较眼熟

ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()

@Adaptive 是标记自适应拓展信息的注解

  • 第一种,标记在上,代表手动实现的 Adaptive 拓展实现类.目前 Dubbo 项目里,只有 ExtensionFactory 拓展的实现类 AdaptiveExtensionFactory 有这么用.

  • 第二种,标记在拓展接口的方法上,代表自动生成代码实现该接口的 Adaptive 拓展实现类.

    • value,从 Dubbo URL 获取参数中,使用键名( Key ),获取键值.该值为真正的拓展名.
    • 自适应拓展实现类,会获取拓展名对应的真正的拓展对象.通过该对象,执行真正的逻辑.
    • 可以设置多个键名( Key ),顺序获取直到有值.若最终获取不到,使用默认拓展名.

    这里作用其实提供默认指定的类, 注解在方法上的好处是可以在运行时根据运行时参数动态生成一个实现类.

@Activate

参考 Dubbo 开发指南 >扩展点加载> 扩展点自动激活

自动激活条件的标记

对于可以被框架中自动激活加载扩展,@Activate 用于配置扩展被自动激活加载条件.比如,Filter 扩展,有多个实现,使用 @Activate 的扩展可以根据条件被自动加载.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
   
    /**
     * Group过滤条件.
     * <br />
     * 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展.
     * <br />
     * 如没有Group设置,则不过滤.
     */
    String[] group() default {};

    /**
     * Key过滤条件.包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展.
     * <p/>
     * 示例: <br/>
     * 注解的值 <code>@Activate("cache,validatioin")</code>,
     * 则{@link ExtensionLoader#getActivateExtension}的URL的参数有<code>cache</code>Key,或是<code>validatioin</code>则返回扩展.
     * <br/>
     * 如没有设置,则不过滤.
     */
    String[] value() default {};


    /**
     * 排序信息,可以不提供.
     */
    String[] before() default {};


    /**
     * 排序信息,可以不提供.
     */
    String[] after() default {};


    /**
     * 排序信息,可以不提供.
     */
    int order() default 0;
}
  • 源码

    public List<T> getActivateExtension(URL url, String key, String group) {
         // 从 Dubbo URL 获得参数值
         String value = url.getParameter(key);
         // 获得符合自动激活条件的拓展对象数组
         return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
    }
     
    /**
      * 获得符合自动激活条件的拓展对象数组
      * @param url    url
      * @param values extension point names
      * @param group  group
      * @return extension list which are activated
      * @see com.alibaba.dubbo.common.extension.Activate
      */
     public List<T> getActivateExtension(URL url, String[] values, String group) {
         List<T> exts = new ArrayList<T>();
         List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
         // 处理自动激活的拓展对象们
         // 判断不存在配置 `"-name"` .例如,<dubbo:service filter="-default" /> ,代表移除所有默认过滤器.
         if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
             // 获得拓展实现类数组
             getExtensionClasses();
             // 循环
             for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
                 String name = entry.getKey();
                 Activate activate = entry.getValue();
                 if (isMatchGroup(group, activate.group())) { // 匹配分组
                     // 获得拓展对象
                     T ext = getExtension(name);
                    if (!names.contains(name) // 不包含在自定义配置里.如果包含,会在下面的代码处理.
                             && !names.contains(Constants.REMOVE_VALUE_PREFIX + name) // 判断是否配置移除.例如 <dubbo:service filter="-monitor" />,则 MonitorFilter 会被移除
                             && isActive(activate, url)) { // 判断是否激活
                         exts.add(ext);
                     }
                 }
             }
             // 排序
             Collections.sort(exts, ActivateComparator.COMPARATOR);
         }
         // 处理自定义配置的拓展对象们.例如在 <dubbo:service filter="demo" /> ,代表需要加入 DemoFilter (这个是笔者自定义的).
        List<T> usrs = new ArrayList<T>();
         for (int i = 0; i < names.size(); i++) {
             String name = names.get(i);
             if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) { // 判断非移除的
                // 将配置的自定义在自动激活的拓展对象们前面.例如,<dubbo:service filter="demo,default,demo2" /> ,则 DemoFilter 就会放在默认的过滤器前面.
                 if (Constants.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;
     }
    

过滤参数用法和@Adaptive 相似, 多了一个分组

总结

本片文章主要讲了SPI的核心实现

  • IOS在SPI的实现
  • AOP在SPI的实现
  • 默认扩展实现类的标记注解@Adaptive
  • 自动激活条件注解@Activate

就这些是不是很简单, 下篇文章我们来写个场景的应用.欢迎关注, 转发, 评论, 点赞~

posted @ 2020-03-22 10:16  AllenAlan  阅读(865)  评论(0编辑  收藏  举报