zoukankan      html  css  js  c++  java
  • dubbo源码阅读之SPI

    dubbo SPI

    SPI,全程Service Provider interface, java中的一种借口扩展机制,将借口的实现类注明在配置文件中,程序在运行时通过扫描这些配置文件从而获取全部的实现类。
    java 原生的spi将借口的实现类信息放在META-INF/services/<借口全限定名>文件中。spi被广泛应用于各种框架及中间件中,用以提升框架的扩展性。
    dubbo也使用spi来提供各种扩展,但是dubbo并未使用java原生的spi,而是实现了自己的spi机制。dubbo的spi实现ExtensionLoader相较于java自带的ServiceLoader还是有不少改进的,例如为每个实现类赋一个名称,以k-v存储在配置文件中,并且在代码中也可以通过名称获取到相应的实现类,另外,ExtensionLoader的加载目录也比ServiceLoader更多,

    ExtensionLoader.getExtensionLoader

    这个方法是入口

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        //做一些非空检查
        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        //必须是接口,spi是针对接口的扩展机制
        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() + "!");
        }
    
        //EXTENSION_LOADERS是ExtensionLoader的全局缓存,interface->ExtensionLoader
        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;
    }
    

    ExtensionLoader.getExtension(String name)

    获取一个ExtensionLoader实例后,通过该方法获取一个扩展类的实例

    private T createExtension(String name) {
        //核心逻辑。找到并加载所有的扩展类
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            //EXTENSION_INSTANCES是扩展类实例的全局缓存
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //自动注入一些属性,实现IOC特性
            injectExtension(instance);
            //对原始实例进行包装,实现AOP特性
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                //层层包装
                //这里有个问题,如果包装类有多个,那么他们的顺序如何???
                //spring中对于多个通知类Advice的情况是会进行排序的
                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 + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }
    

    getExtensionClasses

    //加载该接口所有的扩展类,这个方法主要通过双检查锁机制实现了缓存逻辑,
    
    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

    //这个方法返回的类中不包括带有Adaptive注解的类,以及包装类
    private Map<String, Class<?>> loadExtensionClasses() {
        //缓存默认扩展类的名称
        //默认扩展类名称通过接口上的SPI注解获取,就是SPI注解的value
        cacheDefaultExtensionName();
    
        //将加载的扩展类实例放到该Map中,
        Map<String, Class<?>> extensionClasses = new HashMap<>();
        //加载 META-INF/dubbo/internal/目录下的配置文件
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
        //加载 META-INF/dubbo/internal/目录下alibaba自己的相关接口,通过将org.apache替换为com.alibaba
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        //加载 META-INF/dubbo/目录下的配置文件
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
        //同上
        loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        //加载 META-INF/services/目录下的配置文件
        //这里需要注意的是,由于项目中可能会引入使用jdk  ServiceLoader的包,
        // 那么 META-INF/services/目录下可能存在ServiceLoader的配置文件,而这些文件中存储的实现类并不是以key-value形式存储的
        // 这样ExtensionLoader在加载的时候就找不到name,这个后续会进行一些处理
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
        //同上
        loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
        return extensionClasses;
    }
    

    可以看出加载了META-INF/dubbo/internal/,META-INF/dubbo/,META-INF/services/三个目录下的配置文件,除了加载原接口相关的配置文件,ExtensionLoader额外加载了alibaba自己的相关接口的扩展类。接下来,我们看一下loadDirectory方法。

    loadDirectory

    //从相关的目录找到对应接口类型的配置文件,并加载全部扩展类
    private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        //配置文件全路径:文件夹+接口名称,META
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls;
            //首先获取ClassLoader, 获取顺序是:线程上下文类加载器->加载ExtensionLoader的类加载器->系统类加载器(AppClassLoader)
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                //获取资源文件的url
                urls = classLoader.getResources(fileName);
            } else {
                //多此一举??
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                //遍历找到的所有文件,依次加载进来
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    //加载一个文件中定义的扩展类
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }
    

    loadResource

    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
        try {
            //配置文件必须是utf-8编码格式
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    //#号后面是注释,忽略注释
                    final int ci = line.indexOf('#');
                    if (ci >= 0) {
                        line = line.substring(0, ci);
                    }
                    line = line.trim();
                    if (line.length() > 0) {
                        try {
                            String name = null;
                            //文件中每一行以:name=extension.class.name格式存储扩展类,
                            //以=号作为分隔符,就是properties文件格式
                            int i = line.indexOf('=');
                            //这里少考虑了i==0的情况????
                            //name可以是空,也就是说可以只有扩展类名,而没有name,
                            //实际上这种格式就是ServiceLoader的资源文件,
                            // 对于name为空的情况的处理逻辑在loadClass方法中
                            if (i > 0) {
                                name = line.substring(0, i).trim();
                                line = line.substring(i + 1).trim();
                            }
                            if (line.length() > 0) {
                                //重点关注一下name为空的处理逻辑
                                //这里调用Class.forName加载实现类
                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                            }
                        } catch (Throwable t) {
                            IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                            //记录下加载失败的类,异常及错误信息
                            exceptions.put(line, e);
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }
    

    这里少考虑了i==0的情况????

    loadClass

    //加载一个扩展类
    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        //首先检查是不是相应接口的子类
        if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                    type + ", class line: " + clazz.getName() + "), class "
                    + clazz.getName() + " is not subtype of interface.");
        }
        //设置adaptive, adaptive只能有一个,如果有多个扩展类上都有Adaptive注解,那么会抛异常
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz);
            //保存包装类,
            //判断包装类就是看这个类有没有只有一个参数的构造器,而且参数的类型必须是对应的扩展接口类型
        } else if (isWrapperClass(clazz)) {
            cacheWrapperClass(clazz);
        } else {
            //代码进入这个分支,说明该类是普通的扩展类
            //必须要有无参构造器
            clazz.getConstructor();
            //前面也讲过,name是可以为空的,资源文件中可以只有实现类的类名称
            if (StringUtils.isEmpty(name)) {
                //通过Extension注解找name的值,
                //如果没有Extension注解,那么通过类名称获取name值,具体处理方法不细说
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    //Extension注解中的value为空字符串,这种情况抛异常
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }
            
            //可以有多个别名,类似spring中bean的别名,多个名称以逗号分隔
            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                //如果类带有Activate注解,那么将其缓存下来
                cacheActivateClass(clazz, names[0]);
                //将
                for (String n : names) {
                    //将名字缓存下来,只缓存第一个名字
                    cacheName(clazz, n);
                    //将加载的类放到一路传进来的extensionClasses中,
                    //如果有多个别名,每个别名都存储
                    saveInExtensionClass(extensionClasses, clazz, name);
                }
            }
        }
    }
    

    至此,就查找到了该接口的全部扩展类。

    实现IOC特性

    前面讲到,在createExtension方法中,创建完实例后,会调用injectExtension方法自动组注入一些属性,

    injectExtension

     private T injectExtension(T instance) {
        try {
            //objectFactory是AdaptiveExtensionFactory的一个实例
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    if (isSetter(method)) {
                        /**
                         * Check {@link DisableInject} to see if we need auto injection for this property
                         */
                        if (method.getAnnotation(DisableInject.class) != null) {
                            continue;
                        }
                        Class<?> pt = method.getParameterTypes()[0];
                        if (ReflectUtils.isPrimitives(pt)) {
                            continue;
                        }
                        try {
                            String property = getSetterProperty(method);
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            logger.error("Failed to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }
    

    我们首先看一下objectFactory成员是怎么来的

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

    对于一般的接口类型,objectFactory成员就是带Adaptive注解的ExtensionFactory接口的实现类,其实就是AdaptiveExtensionFactory的一个实例。

    AdaptiveExtensionFactory

    //该类与dubbo spi的自适应机制相关
    @Adaptive
    public class AdaptiveExtensionFactory implements ExtensionFactory {
    
    private final List<ExtensionFactory> factories;
    
    public AdaptiveExtensionFactory() {
        //加载ExtensionFactory接口的所有实现类
        //并缓存到factories中
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        //这里思考:为什么AdaptiveExtensionFactory不会循环调用构造器???
        //原因在ExtensionLoader.loadClass方法中,Adaptive注解的类和包装类都只缓存下来,不在正常的查找扩展类的范围内,
        //getSupportedExtensions实际上返回的是cachedClasses成员保存的类,是不包括Adaptive注解的类和包装类的
        //所以这里才不会发生循环调用AdaptiveExtensionFactory的构造器
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }
    
    @Override
    public <T> T getExtension(Class<T> type, String name) {
        //遍历所有ExtensionFactory实现类,
        //返回第一个不是null的值
        //我们需要看一下都有哪些ExtensionFactory实现类
        //注意,这里思考一下,AdaptiveExtensionFactory为什么不会循环调用???
        //这里仍然涉及到ExtensionFactory多个实现类排序的问题???
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }
    

    }

    这里仍然涉及到ExtensionFactory多个实现类排序的问题???

    这里通过遍历所有的ExtensionFactory实现类,找到相应的属性值。ExtensionFactory实现类有MyExtensionFactory,SpiExtensionFactory,AdaptiveExtensionFactory,SpringExtensionFactory,除去AdaptiveExtensionFactory与自适应机制相关,不起真正的依赖寻找的作用;SpiExtensionFactory是针对带有SPI注解的类型进行自动注入依赖;
    而对与一般的类型,则是使用SpringExtensionFactory来进行依赖注入,在spring的BeanFactory中查找匹配的Bean实例,大概逻辑是:先通过beanName来查找,找不到再通过类型来查找。

    实现AOP特性

    dubbo的aop的实现略显简单,使用静态代理模式,代理类由用户实现,可以通过多层包装实现多级拦截。
    如果有多个包装类的情况下,同样存在顺序的问题, ExtensionLoader有好多地方应该确定类的调用顺序,却没相应的排序规则,甚至都没有预留出排序接口,这点spring做得非常好,
    spring中凡是涉及到多个平级类的链式调用或遍历查找的,都会实现Ordered接口或PriorityOrdered接口,包括aop中有多个通知类Advice的情况,都会以一定的规则进行排序。

  • 相关阅读:
    ExtJs中动态加载机制研究(转)
    ExtJs4 学习3 combox自动加载的例子
    Extjs 4学习2
    ExtJS 4学习
    javascript学习(知识点整理)
    ExtJS智能提示工具spket安装与破解
    eclipse慢 优化(转)
    疯狂学习java web5(SSI框架)
    疯狂学习java web4(jsp)
    疯狂学习java web3(javaScript)
  • 原文地址:https://www.cnblogs.com/zhuge134/p/10793847.html
Copyright © 2011-2022 走看看