其他分享
首页 > 其他分享> > dubbo的SPI扩展机制

dubbo的SPI扩展机制

作者:互联网

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

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

1、Java的SPI扩展机制

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

源码解析部分:

(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<?>接口,将服务的实现的实例化延后到遍历是执行

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懒加载迭代器,主要实现功能:

/**

 * 懒加载迭代器

 */

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扩展机制

 

 

3、SPI在业务代码中的应用

a1290123825 发布了24 篇原创文章 · 获赞 5 · 访问量 1196 私信 关注

标签:dubbo,return,service,迭代,扩展,public,SPI,ServiceLoader,加载
来源: https://blog.csdn.net/a1290123825/article/details/104126848