1.1、JDK SPI&Dubbo SPI扩展机制

  dubbo的SPI(Service Provider Interface)是在java的SPI基础上做了功能特性的扩展;但本质上都是做了和Spring IOC\AOP一样的事,服务(或者bean)的注册统一管理、以及实例化;此外spi提供的是一种jvm级别的服务发现机制,我们只需按照spi的要求,在jar包中进行适当的配置,jvm就会在运行时通过懒加载,帮我们找到所需要的服务并加载。如果我们一直不适用这个服务,那么他就不会被加载,一定程度上避免了资源浪费;

SPI怎么用到我们的业务服务的注册解析管理上?dubbo存在大量的扩展点,业务代码也存在大量扩展点,so.........(此处应该有总结//todo)

1、JDK的SPI扩展机制

使用上就只需要注意下面几点:

  • 虽然没有强制要求使用功能interface或者abstract class作为spi的注册服务,但是作为可扩展服务,尽量养成使用interface或者abstract class的习惯;

  • 必须放在JAR包或项目的指定路径,即 META-INF/services 下;
  • 必须以服务的全限定名命名配置文件,比如本例中,配置文件必须命名为 cn.jinlu.spi.demo.Animal,java会根据此名进行服务查找;
  • 内容必须是一个实现类的全限定名,如果要注册多个实现类,按行分割。注释以#开头。;

源码解析部分:

(1)、代码入口:通过ServiceLoader的静态方法load(Class<?> service)进行服务加载

ServiceLoader的SPI服务注册发现机制

public static void main(String[] args) {
    String content = "";
    // ContentExtractorAware为需要被注册、服务的服务接口,通过静态方法ServiceLoader#load进行加载
    ServiceLoader<ContentExtractorAware> loader = ServiceLoader.load(ContentExtractorAware.class);
    System.out.println("list all the implements sub class to invoke abstract method:\n");
    // 遍历服务的实现
    Iterator iterator = loader.iterator();
    while (iterator.hasNext()) {
        ContentExtractorAware aware = (ContentExtractorAware) iterator.next();
        System.out.println(aware.extract(content));
    }
}

(2)、ServiceLoader构造

默认会使用当前线程的上下文class loader构造ServiceLoader,ServiceLoader通过实现Iterable<?>接口,使得在构造完ServiceLoader后,ServiceLoader实例并不会立刻扫描当前进程中的服务实例,而是实例化创建了一个LazyIterator懒加载迭代器,只有在实际遍历使用时再扫描所有jar包找到对应的服务。懒加载迭代器被保存在一个内部成员lookupIterator中(核心理念);

ServiceLoader

/**
 * 静态方法构建ServiceLoader实例
 *
 * @param service
 * @param <S>
 * @return
 */
public static <S> ServiceLoader<S> load(Class<S> service) {
    // 当前线程上下文class loader
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

/**
 * 构建ServiceLoader实例
 *
 * @param service
 * @param loader
 * @param <S>
 * @return
 */
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
    return new ServiceLoader<>(service, loader);
}

/**
 * 私有构造方法,必须通过ServiceLoader.load(Class<?>)静态方法来创建ServiceLoader实例
 *
 * @param svc
 * @param cl
 */

private ServiceLoader(Class<S> svc, ClassLoader cl) {
    service = Objects.requireNonNull(svc, "Service interface cannot be null");
    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
    reload();
}


/**
 * 重新load指定service的实现。通过LazyIterator实现懒加载。
 */
public void reload() {
    providers.clear();
    lookupIterator = new LazyIterator(service, loader);
}

(3)、服务的加载和遍历

a、ServiceLoader迭代器:ServiceLoader通过实现Iterable<?>接口,将服务的实现的实例化延后到遍历是执行

  • 未进行迭代时,不对jar包做扫描(即不实例化服务实现类)
  • 首次迭代时,Serviceloader.providers中没有任何缓存,总是会通过LazyIterator进行加载,并将service实现的全限定名与加载的service实例作为key-value缓存到ServiceLoader.providers中
  • 之后再进行迭代时,直接从ServiceLoader.providers中获取
    public final class ServiceLoader<S> implements Iterable<S> {
    
         ....
    
        // 缓存的service provider,按照初始化顺序排列。
        private LinkedHashMap<String, S> providers = new LinkedHashMap<>();
        // 懒加载迭代器被保存在一个内部成员lookupIterator中
        // 当前的LazyIterator迭代器指针,服务懒加载迭代器
        private ServiceLoader.LazyIterator lookupIterator;
    
    
        /**
         * 创建ServiceLoader迭代器,隐藏了LazyIterator的实现细节
         *
         * @return
         */
        public Iterator<S> iterator() {
            return new Iterator<S>() {
                // 创建Iterator迭代器时的ServiceLoader.providers快照,
                // 因此在首次迭代时,iterator总是会通过LazyIterator进行懒加载
                Iterator<Map.Entry<String, S>> knownProviders = providers.entrySet().iterator();
                @Override
                public boolean hasNext() {
                    // 如果已经扫描过,则对providers进行迭代;
                    if (knownProviders.hasNext())
                        return true;
                    // 如果没有扫描过,则通过lookupIterator进行扫描和懒加载
                    return lookupIterator.hasNext();
                }
    
                @Override
                public S next() {
                    // 如果已经扫描过,则对providers进行迭代;
                    if (knownProviders.hasNext())
                        return knownProviders.next().getValue();
                    // 如果没有扫描过,则通过lookupIterator进行扫描和懒加载
                    return (S) lookupIterator.next();
                }
    
                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }
       ...
    
    }

     

b、内部私有LazyIterator懒加载迭代器,主要实现功能:

  • 首次迭代时,通过ClassLoader.getResources(String)获得指定services文件的URL集合
  • 如果是首次遍历懒加载器,或者对上一个URL内容解析获得的service实现类集合完成了迭代,则从configs中取下一个services文件URL进行解析,按行获得具体的service实现类集合,并进行迭代。

  • 当前URL中解析得到的实现类集合进行迭代,每次返回一个service实现类。
/**
 * 懒加载迭代器
 */
private class LazyIterator implements Iterator<S> {
    // 服务
    Class<S> service;
 
    // jvm资源加载器
    ClassLoader loader;
 
    // 存放服务全限定名对应资源集合
    Enumeration<URL> configs = null;
 
    // 当前service配置文件的内容迭代器
    // 即对services进行遍历,取出一个services配置文件,再对该文件按行解析,
    // 每行代表一个具体的service实现类,pending是某个services配置文件中service实现类的迭代器
    Iterator<String> pending = null;
    // 下一个服务实现类名称
    String nextName = null;
 
    private LazyIterator(Class<S> service, ClassLoader loader) {
        this.service = service;
        this.loader = loader;
    }
 
 
    @Override
    public boolean hasNext() {
        if (acc == null) {
            return hasNextService();
        } else {
            PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                @Override
                public Boolean run() {
                    return hasNextService();
                }
            };
            return AccessController.doPrivileged(action, acc);
        }
    }
 
 
    // 首次迭代时,configs为空,尝试通过classloader获取名为:
    // "META-INF/services/[服务全限定名]"的所有配置文件
    private boolean hasNextService() {
        if (nextName != null) {
            return true;
        }
        if (configs == null) {
            try {
                // 注意fullName的定义:"META-INF/services/[服务全限定名]"
                String fullName = PREFIX + service.getName();
                if (loader == null)
                    // 通过ClassLoader.getResources()获得资源URL集合
                    configs = ClassLoader.getSystemResources(fullName);
                else
                    configs = loader.getResources(fullName);
            } catch (IOException x) {
                fail(service, "Error locating configuration files", x);
            }
        }
        // 如果pending为空,或者pending已经迭代到迭代器末尾,则尝试解析下一个services配置文件
        while ((pending == null) || !pending.hasNext()) {
            if (!configs.hasMoreElements()) {
                return false;
            }
            pending = parse(service, configs.nextElement());
        }
        // 对当前pending内容进行遍历,每一项代表services的一个实现类
        nextName = pending.next();
        return true;
    }
 
    @Override
    public S next() {
        if (acc == null) {
            return nextService();
        } else {
            PrivilegedAction<S> action = new PrivilegedAction<S>() {
                public S run() {
                    return nextService();
                }
            };
            return AccessController.doPrivileged(action, acc);
        }
    }
 
    private S nextService() {
        if (!hasNextService()){
            throw new NoSuchElementException();
        }
        String cn = nextName;
        nextName = null;
        Class<?> c = null;
        try {
            c = Class.forName(cn, false, loader);
        } catch (ClassNotFoundException x) {
            fail(service,"Provider " + cn + " not found");
        }
        if (!service.isAssignableFrom(c)) {
            fail(service,"Provider " + cn + " not a subtype");
        }
        try {
            S p = service.cast(c.newInstance());
            providers.put(cn, p);
            return p;
        } catch (Throwable x) {
            fail(service,"Provider " + cn + " could not be instantiated",x);
        }
        // This cannot happen
        throw new Error();
    }
    @Override
    public void remove() {
        throw new UnsupportedOperationException();
    }
}

        java的spi最典型的应用就是jdbc的驱动加载,jdbc4.0之前JDBC的开始,总是需要通过Class.forName显式实例化驱动,否则将找不到对应DB的驱动。但是JDBC4.0开始,这个显式的初始化不再是必选项了,它存在的意义只是为了向上兼容。

        为什么JDBC4.0之后不需要了呢?答案就在下面的代码中。在系统启动时,DriverManager静态初始化时会通过ServiceLoader对所有jar包中被注册为 java.sql.Driver 服务的驱动实现类进行初始化,这样就避免了上面通过Class.forName手动初始化的繁琐工作。

public class DriverManager {
 
    // JDBC驱动注册中心,所有加载的JDBC驱动都注册在该CopyOnWriteArrayList中
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
 
    ...
 
    /* Prevent the DriverManager class from being instantiated. */
    private DriverManager(){}
 
    /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     */
    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
 
    private static void loadInitialDrivers() {
        // 如果通过jdbc.drivers配置了驱动,则在本方法最后进行实例化
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                    public String run() {
                        return System.getProperty("jdbc.drivers");
                    }
                    });
        } catch (Exception ex) {
            drivers = null;
        }
        // If the driver is packaged as a Service Provider, load it.
        // Get all the drivers through the classloader
        // exposed as a java.sql.Driver.class service.
        // ServiceLoader.load() replaces the sun.misc.Providers()
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                // 通过ServiceLoader加载所有通过SPI方式注册的"java.sql.Driver"服务
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
                // 遍历ServiceLoader实例进行强制实例化,因此除了遍历不做任何其他操作
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                    // Do nothing
                }
                return null;
                }
            }
        );
 
        println("DriverManager.initialize: jdbc.drivers = " + drivers);
 
        // 强制加载"jdbc.driver"环境变量中配置的DB驱动
        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }
    ...
}

以mySql驱动为例看看驱动实例化时做了什么:

package com.mysql.jdbc;
 
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            // 向DriverManager注册自己
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
 
    /**
     * Construct a new driver and register it with DriverManager
     *
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

因此,只要某个驱动以这种方式被引用并被上下文class loader加载,那么该驱动就会通过SPI的方式被自动发现和加载。实际使用时,Driver.getDriver(url)会通过DB连接url获取到正确的驱动并建立与DB的连接。

2、Dubbo的SPI扩展机制

Dubbo SPI对JDK SPI增强而来,它的增强点,Dubbo 改进了 JDK 标准的 SPI 的以下问题:

  • JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。

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

Dubbo SPI 在 dubbo-common 的 extension 包实现,如下图所示:

代码结构

1、ExtensionLoader属性

com.alibaba.dubbo.common.extension.ExtensionLoader 拓展加载器。这是 Dubbo SPI 的核心。它的属性有:

@SuppressWarnings("deprecation")
public class ExtensionLoader<T> {

    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*");

    // ============================== 静态属性 ==============================

    /**
     * 拓展加载器集合
     *
     * key:拓展接口
     */
    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
    /**
     * 拓展实现类集合
     *
     * key:拓展实现类
     * value:拓展对象。
     *
     * 例如,key 为 Class<AccessLogFilter>
     *      value 为 AccessLogFilter 对象
     */
    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. 带唯一参数为拓展接口的构造方法的实现类,或者说拓展 Wrapper 实现类。例如,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;

    /**
     * 拓展名 与 加载对应拓展类发生的异常 的 映射
     *
     * key:拓展名
     * value:异常
     *
     * 在 {@link #loadFile(Map, String)} 时,记录
     */
    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
  • 在 META-INF/dubbo/internal/ 和 META-INF/dubbo/ 目录下,放置 接口全限定名 配置文件,每行内容为:拓展名=拓展实现类全限定名
  • META-INF/dubbo/internal/ 目录下,从名字上可以看出,用于 Dubbo 内部提供的拓展实现。下图是一个例子:

  • META-INF/dubbo/internal/ 例子
    • META-INF/dubbo/ 目录下,用于用户自定义的拓展实现。
    • META-INF/service/ 目录下,Java SPI 的配置目录。我们会看到 Dubbo SPI 对 Java SPI 做了兼容
  • NAME_SEPARATOR ,拓展名分隔符,使用逗号
  • 我们将属性分成了两类:1)静态属性;2)对象属性。这是为啥呢?
    • 【静态属性】一方面,ExtensionLoader 是 ExtensionLoader 的管理容器。一个拓展( 拓展接口 )对应一个 ExtensionLoader 对象。例如,Protocol 和 Filter 分别对应一个 ExtensionLoader 对象。
    • 【对象属性】另一方面,一个拓展通过其 ExtensionLoader 对象,加载它的拓展实现们。我们会发现多个属性都是 “cached“ 开头。ExtensionLoader 考虑到性能和资源的优化,读取拓展配置后,会首先进行缓存。等到 Dubbo 代码真正用到对应的拓展实现时,进行拓展实现的对象的初始化。并且,初始化完成后,也会进行缓存。也就是说:
      • 缓存加载的拓展配置
      • 缓存创建的拓展实现对象

2、ExtensionLoader获得拓展配置

(1)、getExtensionClasses  #getExtensionClasses() 方法,获得拓展实现类数组。

/**
 * 获得拓展实现类数组
 *
 * @return 拓展实现类数组
 */
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;
}


cachedClasses 属性,缓存的拓展实现类集合。它不包含如下两种类型的拓展实现:

  • 自适应拓展实现类。例如 AdaptiveExtensionFactory 。拓展 Adaptive 实现类,会添加到 cachedAdaptiveClass 属性中。
  • 唯一参数为拓展接口的构造方法的实现类,或者说拓展 Wrapper 实现类。例如,ProtocolFilterWrapper 。拓展 Wrapper 实现类,会添加到 cachedWrapperClasses 属性中。
  • 总结来说,cachedClasses + cachedAdaptiveClass + cachedWrapperClasses 才是完整缓存的拓展实现类的配置。

获取扩展实现类的整体流程如下:

  • cachedClasses.get() 从缓存中,获得拓展实现类数组。
  • 当缓存不存在时,调用 #loadExtensionClasses() 方法,从配置文件中,加载拓展实现类数组。
  • 设置加载的实现类数组,到缓存中。

(2)、loadExtensionClasses   #loadExtensionClasses() 方法,从多个配置文件中,加载拓展实现类数组。

    /**
     * 加载拓展实现类数组
     *
     * 无需声明 synchronized ,因为唯一调用该方法的 {@link #getExtensionClasses()} 已经声明。
     * // synchronized in getExtensionClasses
     *
     * @return 拓展实现类数组
     */
    private Map<String, Class<?>> loadExtensionClasses() {
        // 通过 @SPI 注解,获得默认的拓展实现类名
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((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<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }
  • 首先通过 @SPI 注解,获得拓展接口对应的默认的拓展实现类名。
  • 之后通过logFile从配置文件中加载扩展实现类数组,注意此处配置文件的加载顺序;

(3)loadFile(extensionClasses, dir) 方法,从一个配置文件中,加载拓展实现类数组。代码如下:

private void loadFile(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 url = urls.nextElement();
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                        try {
                            String line;
                            while ((line = reader.readLine()) != null) {
                                // 跳过当前被注释掉的情况,例如 #spring=xxxxxxxxx
                                final int ci = line.indexOf('#');
                                if (ci >= 0) line = line.substring(0, ci);
                                line = line.trim();
                                if (line.length() > 0) {
                                    try {
                                        // 拆分,key=value 的配置格式
                                        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) {
                                            // 判断拓展实现,是否实现拓展接口
                                            Class<?> clazz = Class.forName(line, true, classLoader);
                                            if (!type.isAssignableFrom(clazz)) {
                                                throw new IllegalStateException("Error when load extension class(interface: " +
                                                        type + ", class line: " + clazz.getName() + "), class "
                                                        + clazz.getName() + "is not subtype of interface.");
                                            }
                                            // 缓存自适应拓展对象的类到 `cachedAdaptiveClass`
                                            if (clazz.isAnnotationPresent(Adaptive.class)) {
                                                if (cachedAdaptiveClass == null) {
                                                    cachedAdaptiveClass = clazz;
                                                } else if (!cachedAdaptiveClass.equals(clazz)) {
                                                    throw new IllegalStateException("More than 1 adaptive class found: "
                                                            + cachedAdaptiveClass.getClass().getName()
                                                            + ", " + clazz.getClass().getName());
                                                }
                                            } else {
                                                // 缓存拓展 Wrapper 实现类到 `cachedWrapperClasses`
                                                try {
                                                    clazz.getConstructor(type);
                                                    Set<Class<?>> wrappers = cachedWrapperClasses;
                                                    if (wrappers == null) {
                                                        cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                                                        wrappers = cachedWrapperClasses;
                                                    }
                                                    wrappers.add(clazz);
                                                // 缓存拓展实现类到 `extensionClasses`
                                                } catch (NoSuchMethodException e) {
                                                    clazz.getConstructor();
                                                    // 未配置拓展名,自动生成。例如,DemoFilter 为 demo 。主要用于兼容 Java SPI 的配置。
                                                    if (name == null || name.length() == 0) {
                                                        name = findAnnotationName(clazz);
                                                        if (name == null || name.length() == 0) {
                                                            if (clazz.getSimpleName().length() > type.getSimpleName().length()
                                                                    && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                                                                name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                                                            } else {
                                                                throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                                                            }
                                                        }
                                                    }
                                                    // 获得拓展名,可以是数组,有多个拓展名。
                                                    String[] names = NAME_SEPARATOR.split(name);
                                                    if (names != null && names.length > 0) {
                                                        // 缓存 @Activate 到 `cachedActivates` 。
                                                        Activate activate = clazz.getAnnotation(Activate.class);
                                                        if (activate != null) {
                                                            cachedActivates.put(names[0], activate);
                                                        }
                                                        for (String n : names) {
                                                            // 缓存到 `cachedNames`
                                                            if (!cachedNames.containsKey(clazz)) {
                                                                cachedNames.put(clazz, n);
                                                            }
                                                            // 缓存拓展实现类到 `extensionClasses`
                                                            Class<?> c = extensionClasses.get(n);
                                                            if (c == null) {
                                                                extensionClasses.put(n, clazz);
                                                            } else if (c != clazz) {
                                                                throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    } catch (Throwable t) {
                                        // 发生异常,记录到异常集合
                                        IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
                                        exceptions.put(line, e);
                                    }
                                }
                            } // end of while read lines
                        } finally {
                            reader.close();
                        }
                    } catch (Throwable t) {
                        logger.error("Exception when load extension class(interface: " +
                                type + ", class file: " + url + ") in " + url, t);
                    }
                } // end of while urls
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }
  • 获得完整的文件名( 相对路径 )。例如:"META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory" 。
  • 获得文件名对应的所有文件 URL 数组。例如:ExtensionFactoryExtensionFactory 的配置文件
  • 逐个文件 URL 遍历,逐遍历。跳过当前被 "#" 注释掉的情况,例如#spring=xxxxxxxxx
  • 按照 key=value 的配置拆分。其中 name 为拓展名,line 为拓展实现类名。注意,上文我们提到过 Dubbo SPI 会兼容 Java SPI 的配置格式,那么按照此处的解析方式,name 会为空。这种情况下,拓展名会自动生成。
  • 判断拓展实现类,需要实现拓展接口。
  • 缓存自适应拓展对象的类到 cachedAdaptiveClass 属性。在@Adaptive 详细解析。
  • 缓存拓展 Wrapper 实现类到 cachedWrapperClasses 属性。 a、调用 Class#getConstructor(Class<?>... parameterTypes) 方法,通过反射的方式,参数为拓展接口,判断当前配置的拓展实现类为拓展 Wrapper 实现类。若成功(未抛出异常),则代表符合条件。例如,ProtocolFilterWrapper(Protocol protocol) 这个构造方法。
  • 若获得构造方法失败,则代表是普通的拓展实现类,缓存到 extensionClasses 变量中。
  • 调用 Class#getConstructor(Class<?>... parameterTypes) 方法,获得参数为空的构造方法。
  • 未配置拓展名,自动生成。适用于 Java SPI 的配置方式。例如,xxx.yyy.DemoFilter 生成的拓展名为 demo 。
    • 通过 @Extension 注解的方式设置拓展名的方式已经废弃,胖友可以无视该方法。
  • 获得拓展名。使用逗号进行分割,即多个拓展名可以对应同一个拓展实现类。
  • 缓存 @Activate 到 cachedActivates 。
  • 缓存到 cachedNames 属性。
  • 缓存拓展实现类到 extensionClasses 变量。注意,相同拓展名,不能对应多个不同的拓展实现。
  • 若发生异常,记录到异常集合 exceptions 属性。

2、获得拓展加载器

在 Dubbo 的代码里,常常能看到如下的代码:

ExtensionLoader.getExtensionLoader(Class<T> type){
   // ...
}

(1)、 getExtensionLoader

#getExtensionLoader(type) 静态方法,根据拓展点的接口,获得拓展加载器(必须是接口)。代码如下:

    /**
     * 根据拓展点的接口,获得拓展加载器
     *
     * @param type 接口
     * @param <T> 泛型
     * @return 加载器
     */
    @SuppressWarnings("unchecked")
    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 interface!");
        }
        // 必须包含 @SPI 注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }

        // 获得接口对应的拓展点加载器
        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;
    }
  • 调用 withExtensionAnnotation方法,校验必须使用 @SPI 注解标记。
  • 从 EXTENSION_LOADERS 静态中获取拓展接口对应的 ExtensionLoader 对象。若不存在,则创建 ExtensionLoader 对象,并添加到 EXTENSION_LOADERS

(2)、构造方法

构造方法,代码如下:

    /**
     * 拓展接口。
     * 例如,Protocol
     */
    private final Class<?> type;
    
    /**
     * 对象工厂
     *
     * 用于调用 {@link #injectExtension(Object)} 方法,向拓展对象注入依赖的属性。
     *
     * 例如,StubProxyFactoryWrapper 中有 `Protocol protocol` 属性。
     */
    private final ExtensionFactory objectFactory;
    
    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
  • objectFactory 属性,对象工厂,功能上和 Spring IOC 一致

3、 获得指定拓展对象

在 Dubbo 的代码里,常常能看到如下的代码:

ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)

#getExtension() 方法,返回指定名字的扩展对象。如果指定名字的扩展不存在,则抛异常 IllegalStateException 。代码如下:

   /**
     * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
     * will be thrown.
     */
    /**
     * 返回指定名字的扩展对象。如果指定名字的扩展不存在,则抛异常 {@link IllegalStateException}.
     *
     * @param name 拓展名
     * @return 拓展对象
     */
    @SuppressWarnings("unchecked")
    public T getExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        // 查找 默认的 拓展对象
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        // 从 缓存中 获得对应的拓展对象
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(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;
    }

 

  • 调用 #getDefaultExtension() 方法,查询默认的拓展对象。在该方法的实现代码中,简化代码为 getExtension(cachedDefaultName); 。
  • 从缓存中,获得拓展对象。
  • 当缓存不存在时,调用 #createExtension(name) 方法,创建拓展对象。
  • 添加创建的拓展对象,到缓存中。

(2)、createExtension  #createExtension(name) 方法,创建拓展名的拓展对象,并缓存。代码如下:

 /**
     * 创建拓展名的拓展对象,并缓存。
     *
     * @param name 拓展名
     * @return 拓展对象
     */
    @SuppressWarnings("unchecked")
    private T createExtension(String name) {
        // 获得拓展名对应的拓展实现类
        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);
            // 创建 Wrapper 拓展对象
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                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 + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }
  • 获得拓展名对应的拓展实现类。若不存在,调用 #findException(name) 方法,抛出异常。
  • 从缓存 EXTENSION_INSTANCES 静态属性中,获得拓展对象。
  • 当缓存不存在时,创建拓展对象,并添加到 EXTENSION_INSTANCES 中。因为 #getExtension(name) 方法中已经加 synchronized 修饰,所以此处不用同步。
  • 调用 #injectExtension(instance) 方法,向创建的拓展注入其依赖的属性。
  • 创建 Wrapper 拓展对象,将 instance 包装在其中。在 《Dubbo 开发指南 —— 扩展点加载》 文章中,如此介绍 Wrapper 类:

    Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。

    扩展点的 Wrapper 类可以有多个,也可以根据需要新增。

    通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。

    • 例如:ListenerExporterWrapper、ProtocolFilterWrapper 。

(3)、injectExtension #injectExtension(instance) 方法,注入依赖的属性。代码如下:

 /**
     * 注入依赖的属性
     *
     * @param instance 拓展对象
     * @return 拓展对象
     */
    private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    if (method.getName().startsWith("set")
                            && method.getParameterTypes().length == 1
                            && Modifier.isPublic(method.getModifiers())) { // setting && public 方法
                        // 获得属性的类型
                        Class<?> pt = method.getParameterTypes()[0];
                        try {
                            // 获得属性
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            // 获得属性值
                            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 的拓展对象,不需要注入依赖的属性。
  • 反射获得所有的方法,仅仅处理 public setting 方法。
  • 获得属性的类型。获得属性名。获得属性值注意,此处虽然调用的是ExtensionFactory#getExtension(type, name)方法,实际获取的不仅仅是拓展对象,也可以是 Spring Bean 对象。设置属性值。

4、获得自适应的拓展对象

在 Dubbo 的代码里,常常能看到如下的代码:

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

友情提示,先看下 Adaptive 的内容,在回到此处。getAdaptiveExtension

(1)、getAdaptiveExtension #getAdaptiveExtension() 方法,获得自适应拓展对象。

 /**
     * 获得自适应拓展对象
     *
     * @return 拓展对象
     */
    @SuppressWarnings("unchecked")
    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("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            // 若之前创建报错,则抛出异常 IllegalStateException
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }
        return (T) instance;
    }
  • 从缓存 cachedAdaptiveInstance 属性中,获得自适应拓展对象。若之前创建报错,则抛出异常 IllegalStateException 。
  • 当缓存不存在时,调用 #createAdaptiveExtension() 方法,创建自适应拓展对象,并添加到 cachedAdaptiveInstance 中。
  • 若创建发生异常,记录异常到 createAdaptiveInstanceError ,并抛出异常IllegalStateException 。

(2)、createAdaptiveExtension #createAdaptiveExtension() 方法,创建自适应拓展对象。代码如下:

    /**
     * 创建自适应拓展对象
     *
     * @return 拓展对象
     */
    @SuppressWarnings("unchecked")
    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
  • 调用 #getAdaptiveExtensionClass() 方法,获得自适应拓展类。
  • 调用 Class#newInstance() 方法,创建自适应拓展对象。
  • 调用 #injectExtension(instance) 方法,向创建的自适应拓展对象,注入依赖的属性。

(3)、 getAdaptiveExtensionClass #getAdaptiveExtensionClass() 方法,获得自适应拓展类。代码如下:

    /**
     * @return 自适应拓展类
     */
    private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
  • @Adaptive 的第一种】:若 cachedAdaptiveClass 已存在,直接返回。的第一种情况。
  • @Adaptive 的第二种】:调用 #createAdaptiveExtensionClass() 方法,自动生成自适应拓展的代码实现,并编译后返回该类。

(4)createAdaptiveExtensionClassCode #createAdaptiveExtensionClassCode() 方法,自动生成自适应拓展的代码实现,并编译后返回该类。

    /**
     * 自动生成自适应拓展的代码实现,并编译后返回该类。
     *
     * @return 类
     */
    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);
    }
  • 调用 #createAdaptiveExtensionClassCode 方法,自动生成自适应拓展的代码实现的字符串;
    代码比较简单,已经添加详细注释
    如下是 ProxyFactory 的自适应拓展的代码实现的字符串生成例子;

自适应拓展的代码实现的字符串生成例子

  • 使用 Dubbo SPI 加载 Compier 拓展接口对应的拓展实现对象,后调用 Compiler#compile(code, classLoader) 方法,进行编译。🙂 因为不是本文的重点,后续另开文章分享。

5、 获得激活的拓展对象数组

在 Dubbo 的代码里,看到使用代码如下:

List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

(1)、getExtensionLoader #getExtensionLoader(url, key, group) 方法,获得符合自动激活条件的拓展对象数组。

    /**
     * This is equivalent to {@code getActivateExtension(url, url.getParameter(key).split(","), null)}
     *
     * 获得符合自动激活条件的拓展对象数组
     *
     * @param url   url
     * @param key   url parameter key which used to get extension point names
     *              Dubbo URL 参数名
     * @param group group
     *              过滤分组名
     * @return extension list which are activated.
     * @see #getActivateExtension(com.alibaba.dubbo.common.URL, String[], String)
     */
    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);
    }
 /**
     * Get activate extensions.
     *
     * 获得符合自动激活条件的拓展对象数组
     *
     * @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;
    }
  • 从 Dubbo URL 获得参数值。例如说,若 XML 配置 Service <dubbo:service filter="demo, demo2" /> ,并且在获得 Filter 自动激活拓展时,此处就能解析到 value=demo,demo2 。另外,value 可以根据逗号拆分。
  • 调用 #getActivateExtension(url, values, group) 方法,获得符合自动激活条件的拓展对象数组。
  • 处理自动激活的拓展对象们。
  • #isMatchGroup(group, groups) 方法,匹配分组。
  • #isActive(Activate, url) 方法,是否激活,通过 Dubbo URL 中是否存在参数名为 `@Activate.value` ,并且参数值非空。
  • 处理自定义配置的拓展对象们。
  • 将 usrs 合并到 exts 尾部

(2)、ActivateComparator

  • com.alibaba.dubbo.common.extension.support.ActivateComparator , 自动激活拓展对象排序器。
  • 代码比较简单,直接看注释。

3、 @SPI

com.alibaba.dubbo.common.extension.@SPI,扩展点接口的标识。代码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {

/**
* default extension name
*/
String value() default "";

}
  • value ,默认拓展实现类的名字。例如,Protocol 拓展接口,代码如下:

    @SPI("dubbo")
    public interface Protocol {
    // ... 省略代码
    }
    • 其中 "dubbo" 指的是 DubboProtocol ,Protocol 默认的拓展实现类。

4、 @Adaptive

com.alibaba.dubbo.common.extension.@Adaptive ,自适应拓展信息的标记。代码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {

/**
* Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
* in the URL, and the parameter names are given by this method.
* <p>
* If the specified parameters are not found from {@link URL}, then the default extension will be used for
* dependency injection (specified in its interface's {@link SPI}).
* <p>
* For examples, given <code>String[] {"key1", "key2"}</code>:
* <ol>
* <li>find parameter 'key1' in URL, use its value as the extension's name</li>
* <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
* <li>use default extension if 'key2' doesn't appear either</li>
* <li>otherwise, throw {@link IllegalStateException}</li>
* </ol>
* If default extension's name is not give on interface's {@link SPI}, then a name is generated from interface's
* class name with the rule: divide classname from capital char into several parts, and separate the parts with
* dot '.', for example: for {@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}, its default name is
* <code>String[] {"yyy.invoker.wrapper"}</code>. This name will be used to search for parameter from URL.
*
* @return parameter key names in URL
*/
/**
* 从 {@link URL }的 Key 名,对应的 Value 作为要 Adapt 成的 Extension 名。
* <p>
* 如果 {@link URL} 这些 Key 都没有 Value ,使用 缺省的扩展(在接口的{@link SPI}中设定的值)。<br>
* 比如,<code>String[] {"key1", "key2"}</code>,表示
* <ol>
* <li>先在URL上找key1的Value作为要Adapt成的Extension名;
* <li>key1没有Value,则使用key2的Value作为要Adapt成的Extension名。
* <li>key2没有Value,使用缺省的扩展。
* <li>如果没有设定缺省扩展,则方法调用会抛出{@link IllegalStateException}。
* </ol>
* <p>
* 如果不设置则缺省使用Extension接口类名的点分隔小写字串。<br>
* 即对于Extension接口 {@code com.alibaba.dubbo.xxx.YyyInvokerWrapper} 的缺省值为 <code>String[] {"yyy.invoker.wrapper"}</code>
*
* @see SPI#value()
*/
String[] value() default {};

}

@Adaptive 注解,可添加方法上,分别代表了两种不同的使用方式。

友情提示:一个拓展接口,有且仅有一个 Adaptive 拓展实现类。

  • 第一种,标记在上,代表手动实现它是一个拓展接口的 Adaptive 拓展实现类。目前 Dubbo 项目里,只有 ExtensionFactory 拓展的实现类 AdaptiveExtensionFactory 有这么用。
  • 第二种,标记在拓展接口的方法上,代表自动生成代码实现该接口的 Adaptive 拓展实现类。
    • value ,从 Dubbo URL 获取参数中,使用键名( Key ),获取键值。该值为真正的拓展名。
      • 自适应拓展实现类,会获取拓展名对应的真正的拓展对象。通过该对象,执行真正的逻辑。
      • 可以设置多个键名( Key ),顺序获取直到有值。若最终获取不到,使用默认拓展名

5、 @Activate

com.alibaba.dubbo.common.extension.@Activate ,自动激活条件的标记。代码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* Activate the current extension when one of the groups matches. The group passed into
* {@link ExtensionLoader#getActivateExtension(URL, String, String)} will be used for matching.
*
* @return group names to match
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
/**
* Group过滤条件。
* <br />
* 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
* <br />
* 如没有Group设置,则不过滤。
*/
String[] group() default {};

/**
* Activate the current extension when the specified keys appear in the URL's parameters.
* <p>
* For example, given <code>@Activate("cache, validation")</code>, the current extension will be return only when
* there's either <code>cache</code> or <code>validation</code> key appeared in the URL's parameters.
* </p>
*
* @return URL parameter keys
* @see ExtensionLoader#getActivateExtension(URL, String)
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
/**
* 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 {};

/**
* Relative ordering info, optional
*
* @return extension list which should be put before the current one
*/
/**
* 排序信息,可以不提供。
*/
String[] before() default {};

/**
* Relative ordering info, optional
*
* @return extension list which should be put after the current one
*/
/**
* 排序信息,可以不提供。
*/
String[] after() default {};

/**
* Absolute ordering info, optional
*
* @return absolute ordering info
*/
/**
* 排序信息,可以不提供。
*/
int order() default 0;
}
  • 对于可以被框架中自动激活加载扩展,@Activate 用于配置扩展被自动激活加载条件。比如,Filter 扩展,有多个实现,使用 @Activate 的扩展可以根据条件被自动加载。
  • 分成过滤条件和排序信息两类属性,胖友看下代码里的注释。

6、 ExtensionFactory

com.alibaba.dubbo.common.extension.ExtensionFactory ,拓展工厂接口。代码如下:
/**
* ExtensionFactory
*
* 拓展工厂接口
*/
@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);

}
  • ExtensionFactory 自身也是拓展接口,基于 Dubbo SPI 加载具体拓展实现类#getExtension(type, name)方法,在 「4.4.3 injectExtension」 中,获得拓展对象,向创建拓展对象注入依赖属性。在实际代码中,我们可以看到不仅仅获得的是拓展对象,也可以是 Spring 中的 Bean 对象。
    ExtensionFactory 子类类图如下:ExtensionFactory 类图

1、AdaptiveExtensionFactory com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory ,自适应 ExtensionFactory 拓展实现类。代码如下:

/**
 * AdaptiveExtensionFactory
 */
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    /**
     * ExtensionFactory 拓展对象集合
     */
    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        // 使用 ExtensionLoader 加载拓展对象实现类。
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    public <T> T getExtension(Class<T> type, String name) {
        // 遍历工厂数组,直到获得到属性
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}
  • @Adaptive 注解,为 ExtensionFactory 的自适应拓展实现类。
  • 构造方法,使用 ExtensionLoader 加载 ExtensionFactory 拓展对象的实现类。若胖友没自己实现 ExtensionFactory 的情况下,factories 为 SpiExtensionFactory 和 SpringExtensionFactory 。
  • #getExtension(type, name) 方法,遍历 factories ,调用其 #getExtension(type, name) 方法,直到获得到属性值。

2、 SpiExtensionFactory

com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory ,SPI ExtensionFactory 拓展实现类。代码如下:
public class SpiExtensionFactory implements ExtensionFactory {

/**
* 获得拓展对象
*
* @param type object type. 拓展接口
* @param name object name. 拓展名
* @param <T> 泛型
* @return 拓展对象
*/
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { // 校验是 @SPI
// 加载拓展接口对应的 ExtensionLoader 对象
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
// 加载拓展对象
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}

}
3、 SpringExtensionFactory
com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory,Spring ExtensionFactory 拓展实现类。代码如下:
/**
 * SpringExtensionFactory
 */
public class SpringExtensionFactory implements ExtensionFactory {

    /**
     * Spring Context 集合
     */
    private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>();

    public static void addApplicationContext(ApplicationContext context) {
        contexts.add(context);
    }

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

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getExtension(Class<T> type, String name) {
        for (ApplicationContext context : contexts) {
            if (context.containsBean(name)) {
                // 获得属性
                Object bean = context.getBean(name);
                // 判断类型
                if (type.isInstance(bean)) {
                    return (T) bean;
                }
            }
        }
        return null;
    }

}
  • #getExtension(type, name) 方法,遍历 contexts ,调用其 ApplicationContext#getBean(name) 方法,获得 Bean 对象,直到成功并且值类型正确。

 例子:DemoFilter 是自定义实现的 Filter 拓展实现类,代码如下:

public class DemoFilter implements Filter {

    private DemoDAO demoDAO;

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        return invoker.invoke(invocation);
    }

    public DemoFilter setDemoDAO(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
        return this;
    }
}
  • DemoDAO ,笔者在 Spring 中声明对应的 Bean 对象。

    <bean id="demoDAO" class="com.alibaba.dubbo.demo.provider.DemoDAO" />
  • 在 「4.4.3 injectExtension」中,会调用 #setDemoDAO(demo) 方法,将 DemoFilter 依赖的属性 demoDAO 注入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值