深入Dubbo(一)——Dubbo SPI & Java SPI对比、应用、源码解析

本文详细探讨了Dubbo SPI和Java SPI的区别,从代码应用、源码分析到问题分析,揭示了Dubbo SPI作为核心机制的重要性。内容包括Java SPI的资源消耗、灵活性不足和线程不安全问题,以及Dubbo SPI的扩展点加载、自适应编译和IoC与AOP原理。通过深入理解,有助于提升对Dubbo设计思想的认识。

目录​​​​​

前言

一、Dubbo SPI&Java SPI的脑图

二、Java SPI

1. 代码应用

2.源码分析

3.问题分析

三、Dubbo SPI

1、基础应用

2、源码分析

总结



前言

SPI是什么?解决了什么问题?Dubbo 为什么要实现SPI?Java SPI有什么问题?

看完本文您应该对此有个大致了解,并且Dubbo SPI作为Dubbo核心机制,了解Dubbo SPI的使用与实现原理,对我们深入理解Dubbo的整体设计思想会很大的帮助。


  

一、Dubbo SPI&Java SPI的脑图

先上图,方便大家快速定位所需内容(如果脑图实用,欢迎点赞留言)。

二、Java SPI

1. 代码应用

  1. 定义接口类
    public interface JavaSPIInterface {
        String echo(String msg);
    }
  2. 定义实现类:
    // 以下3个实现类应该分为三个Java文件,为了方便我放在了一个代码段中
    // 实现类1
    public class JavaSPIInterfaceImpl1 implements JavaSPIInterface {
        @Override
        public String echo(String msg) {
            return "JavaSPIInterfaceImpl1 -- " + msg;
        }
    }
    
    // 实现类2
    public class JavaSPIInterfaceImpl2  implements JavaSPIInterface {
        @Override
        public String echo(String msg) {
            return "JavaSPIInterfaceImpl2 -- " + msg;
        }
    }
    
    // 实现类3
    public class JavaSPIInterfaceImpl3 implements JavaSPIInterface {
        @Override
        public String echo(String msg) {
            return "JavaSPIInterfaceImpl3 -- " + msg;
        }
    }
  3. 配置扩展点,配置文件存META-INF/services/com.xxx.JavaSPIInterface(即接口的全路径):
    # 扩展点接口实现的全路径
    com.xxx.impl.JavaSPIInterfaceImpl1
    com.xxx.impl.JavaSPIInterfaceImpl2
    com.xxx.impl.JavaSPIInterfaceImpl3
  4. 单元测试类

    ServiceLoader<JavaSPIInterface> javaSPIInterfaces = ServiceLoader.load(JavaSPIInterface.class);        
    Iterator<JavaSPIInterface> iterator = javaSPIInterfaces.iterator();
    while (iterator.hasNext()){
       System.out.println(iterator.next().echo("sks-9527"));
    }

    如上的4个步骤就是Java SPI的核心流程。

2.源码分析

  1. ServiceLoader.load(JavaSPIInterface.class);源码解析:
    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();
        }
    .....
    public void reload() {
            providers.clear();
            //最终是创建了一个LazyIterator 实例,此时【并没有】发生扩展点文件的加载以及扩展点实现类的实例化
            lookupIterator = new LazyIterator(service, loader);
        }
    .....
    private LazyIterator(Class<S> service, ClassLoader loader) {
                this.service = service;
                this.loader = loader;
            }
  2. LazyIterator#hasNext源码解析:

    public Iterator<S> iterator() {
            return new Iterator<S>() {
                // 获取SerivceLoader属性LinkedHashMap<String,S> providers的迭代器,
                //LinkedHashMap<String,S> providers用于存储扩展点的实例(key为实现类的全路径,value为对应实例)
                Iterator<Map.Entry<String,S>> knownProviders
                    = providers.entrySet().iterator();
    
                public boolean hasNext() {
                    // knownProviders.hasNext() 为true,表示缓存存在数据,不需要lookupIterator进行再次加载。
                    // 为false,则需要加载扩展点配置来判断是否存在下一个实例。
                    if (knownProviders.hasNext())
                        return true;
                    // 加载扩展点配置文件
                    return lookupIterator.hasNext();
                }
    
                public S next() {
                    // 缓存实例存在数据,则直接返回,避免重复实例化扩展点实现
                    if (knownProviders.hasNext())
                        return knownProviders.next().getValue();
                    // 加载扩展点实现
                    return lookupIterator.next();
                }
    
                public void remove() {
                    throw new UnsupportedOperationException();
                }
    
            };
        }
    
    .......
    // lookupIterator.hasNext(); 最终调用如下代码
    private boolean hasNextService() {
         if (nextName != null) {
            return true;
         }
         // Enumeration<URL> configs 扩展点的文件资源,
         // 为null时则进行加载,不为null,则进行获取扩展点文件的内容
         // 很明显——【线程不安全的操作】
         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);
               }
          }
          // pending 扩展点配置类全路径字符串的迭代器
          while ((pending == null) || !pending.hasNext()) {
             if (!configs.hasMoreElements()) {
                 return false;
             }
             // 解析配置文件内容
             pending = parse(service, configs.nextElement());
          }
             // 获取扩展点实现类的下一个全路径字符串,
             // 在lookupIterator#next中会根据nextName进行扩展点实现类的实例化
             nextName = pending.next();
             return true;
    }
    .....
    // 加载扩展点配置文件内容
    private Iterator<String> parse(Class<?> service, URL u)
            throws ServiceConfigurationError
        {
            InputStream in = null;
            BufferedReader r = null;
            // 存储扩展点配置文件每一行(扩展点实现类的全路径)的字符串
            ArrayList<String> names = new ArrayList<>();
            try {
                in = u.openStream();
                r = new BufferedReader(new InputStreamReader(in, "utf-8"));
                int lc = 1;
                while ((lc = parseLine(service, u, r, lc, names)) >= 0);
            } catch (IOException x) {
                
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值