Dubbo源码分析之SPI

背景

在Dubbo源码学习过程中,首先就遇到了spi机制,看了一下别人写的介绍,感觉还是基础的,而且它在jdbc中也有应用。但我却感觉很陌生。总之Java基础需要不断加强,下面总结一下spi。

一、别人已经做了不错的总结了,参考文章
二、使用demo
1.项目java/main/resources/META-INF/services目录下新建文本文件,文件名为com.alibaba.dubbo.cache.CacheFactory;
2.刚才文件中的文本内容为
        com.alibaba.dubbo.cache.support.jcache.JCacheFactory
        com.alibaba.dubbo.cache.support.lru.LruCacheFactory
3.测试类
public class SpiTest {
    public static void main(String[] args){
        ServiceLoader<CacheFactory> load = ServiceLoader.load(CacheFactory.class);   //ServiceLoader在java.util包中
        Iterator<CacheFactory> iterator = load.iterator();
        while(iterator.hasNext()){
            CacheFactory next = iterator.next();
            System.out.println(next);
        }
    }
}

4.运行效果如下
com.alibaba.dubbo.cache.support.jcache.JCacheFactory@3f99bd52
com.alibaba.dubbo.cache.support.lru.LruCacheFactory@7adf9f5f

Process finished with exit code 0
三、spi源码分析

这么一个简单的例子初看也不知道是干嘛的。感觉就是编写一个接口和几个实现类,然后再根据规则定义个配置文件,最后程序能够根据接口和配置文件,得到所有实现类的实例对象,底层涉及到反射技术。但是具体怎么用?以及Dubbo中spi扩展机制又是什么?这些需要一点点理解消化。先不扯那么远,先来把这个小例子的源码看看,毕竟千里之行始于当下。

<1>ServiceLoader.load(CacheFactory.class)

    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

继续

    public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }

继续

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

有必要看看ServiceLoad的成员方法了,如下

public final class ServiceLoader<S>
    implements Iterable<S>
{

    private static final String PREFIX = "META-INF/services/";

    // The class or interface representing the service being loaded
    private final Class<S> service;

    // The class loader used to locate, load, and instantiate providers
    private final ClassLoader loader;

    // The access control context taken when the ServiceLoader is created
    private final AccessControlContext acc;

    // Cached providers, in instantiation order
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

    // The current lazy-lookup iterator
    private LazyIterator lookupIterator;
...
}

继续

    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

继续,来看看这个LazyIterator类

小总结:到这里ServiceLoader.load(CacheFactory.class)源码涉及就这多么,关键就是初始化了LazyIterator类(ServiceLoad的私有内部类)的lookupIterator对象,这个对象看样子好像会比较有用。

<2>load.iterator()

    public Iterator<S> iterator() {
        return new Iterator<S>() {               //返回了一个迭代器A

            Iterator<Map.Entry<String,S>> knownProviders
                = providers.entrySet().iterator();     //注意,这个providers是类初始化new的一个空的Map,什么时候真正赋值呢?这里需要注意一下!

            public boolean hasNext() {
                if (knownProviders.hasNext())
                    return true;
                return lookupIterator.hasNext();   //迭代器A的hasNext()方法真正调用的是前面分析的lookupIterator对象的hasNext方法
            }

            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next();      ////迭代器A的next()方法真正调用的是前面分析的lookupIterator对象的next方法
            }

            public void remove() {
                throw new UnsupportedOperationException();  
            }

        };
    }

小总结:Serviceload返回的迭代器,实际是对前面分析中关键对象lookupIterator的封装。所以关键点就是这个私有内部类的实现了。下面继续对它分析。

<3>LazyIterator私有内部类源码

 private class LazyIterator implements Iterator<S>
    {
        Class<S> service;   //接口类对象
        ClassLoader loader;  
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;

        private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }

        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }

        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());    //Class类中的cast(Object obj),强转
                providers.put(cn, p);                   //这里解答了前面提到的providers真正初始话的问题。
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

        public boolean hasNext() {
            if (acc == null) {      //权限这里先不考虑
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

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

        public void remove() {
            throw new UnsupportedOperationException();
        }

    }

小总结:底层果然是反射,先分析到这

四、JDBC中应用
String url = "jdbc:mysql:///consult?serverTimezone=UTC";
String user = "root";
String password = "root";

Class.forName("com.mysql.jdbc.Driver");   //新的不需要,但有也能运行
Connection connection = DriverManager.getConnection(url, user, password);

新版JDBC不再需要Class.forName()来显式加载jdbc驱动,底层就是利用spi技术提前加载好了。下面是我源码总结图

小总结:这篇文章仅仅小小总结了一下spi,希望以后继续更新更多spi的实际应用分析。回到dubbo学习主线吧(20200309)

posted on 2020-03-09 17:48  火枪  阅读(87)  评论(0编辑  收藏  举报