美文网首页
【Dubbo】SPI(2)

【Dubbo】SPI(2)

作者: 半个橙子 | 来源:发表于2018-06-07 16:14 被阅读0次

    dubbo自定义SPI

    为什么dubbo要自己设计一套SPI?

    1. JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,而且没用上也加载,会很浪费资源;
    2. 增加了对扩展点IOC和AOP的支持,一个扩展点可以直接setter注入其它扩展点。
    • 这是原始JDK spi的代码
      ServiceLoader<Command> serviceLoader=ServiceLoader.load(Command.class); 
      for(Command command:serviceLoader){  
          command.execute();  
      }  
    

    dubbo在原来的基础上设计了以下功能:

    1. 原始JDK spi不支持缓存;dubbo设计了缓存对象:spi的key与value 缓存在 cachedInstances对象里面,它是一个ConcurrentMap;
    2. 原始JDK spi不支持默认值,dubbo设计默认值:@SPI("dubbo") 代表默认的spi对象。例如:
      Protocol的@SPI("dubbo")就是 DubboProtocol,通过 ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension()拿默认对象
    3. jdk要用for循环判断对象,dubbo设计getExtension灵活方便,动态获取spi对象。例如: ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi的key)来提取对象;
    4. 原始JDK spi不支持 AOP功能,dubbo设计增加了AOP功能,在cachedWrapperClasses,在原始spi类,包装了XxxxFilterWrapper XxxxListenerWrapper
    5. 原始JDK spi不支持 IOC功能,dubbo设计增加了IOC,通过set注入:injectExtension(T instance)

    Dubbo的SPI约定

    扩展点的配置都放到\META-INF\dubbo\internal路径下,且文件名为扩展接口的包名+接口名;文件内容为:扩展名=实现类的包名+类名,例Protocol的SPI配置:
    \META-INF\dubbo\internal\com.alibaba.dubbo.rpc.Protocol

    injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
    registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
    dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
    filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
    listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
    mock=com.alibaba.dubbo.rpc.support.MockProtocol
    

    扩展点类型

    dubbo为了实现IOC和AOP,设计了多种功能不同的扩展点。我们可以根据扩展点功能的不同把扩展点大致分成4类:

    扩展点类型 说明
    Adaptive 含有Adaptive注解的扩展点
    Wrapper 含有构造函数的参数为扩展点接口类型的扩展点
    1.Filter 2.Listener
    Activate 含有Activate注解的扩展点
    其他 普通扩展点
    扩展点层级

    如Protocol的所有扩展点如下:

    injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
    registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
    dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
    filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
    listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
    mock=com.alibaba.dubbo.rpc.support.MockProtocol
    

    我们可以按照上面的规则将所有的Protocol扩展点分类:

    扩展点类型 名称
    Adaptive Protocol$Adaptive(动态编译)
    Wrapper filter,listener
    其他 injvm,registry,dubbo,mock

    通过ExtensionLoader.getExtensionLoader(Protpcol.class).getAdaptiveExtension()创建Protpcol的默认扩展点“dubbo”,而如果我们调用得到的这个实例的方法,它的实际调用流程却是
    Protocol$Adaptive->ProtocolFilterWrapper->ProtocolListenerWrapper->DubboProtocol
    似乎跟Spring的AOP很像,跟一下源码,来一探究竟。

    源码分析

    • dubbo spi 的目的:获取一个指定实现类的对象。
    • 途径:ExtensionLoader.getExtension(String name)
    • 实现路径:
      getExtensionLoader(Class<T> type)就是为该接口new 一个ExtensionLoader,然后缓存起来。
      getAdaptiveExtension() 获取一个扩展类,如果@Adaptive注解在类上就是一个装饰类;如果注解在方法上就是一个动态代理类,例如Protocol$Adaptive对象。
      getExtension(String name) 获取一个指定扩展点的对象。

    在dubbo的源码中随处可见获取扩展点的例子,我们看下面加载Protocol.class的扩展点dubbo的例子。

    package com.alibaba.dubbo.rpc;
    @SPI("dubbo")
    public interface Protocol {
        int getDefaultPort();
        @Adaptive
        <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
        @Adaptive
        <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
        void destroy();
    }
    
    • META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol中可以知道Protocol.class有很多扩展点,其中就有名为dubbo的扩展点。
    injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
    registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
    dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
    filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
    listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
    mock=com.alibaba.dubbo.rpc.support.MockProtocol
    

    1. 获取扩展点加载器

    首先认识一下扩展点加载器,他的静态变量是一些全局的缓存,私有变量会在第一次获取该扩展点实例的时候初始化

    public class ExtensionLoader<T> {
        private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
        /***所有ExtensionLoader共享变量***/
        //扩展点的文件路径
        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*");
        //缓存所有的ExtensionLoader,key是扩展点的接口类型
        private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
        //缓存扩展点的一个实例
        private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
    
        // ==============================
        /*****当前ExtensionLoader的私有变量****/
    //扩展点的接口类型
        private final Class<?> type;
    //扩展点IOC的实例来源
        private final ExtensionFactory objectFactory;
        //普通扩展点的名称
        private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
        private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String,Class<?>>>();
        private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
        //adaptive的类
        private volatile Class<?> cachedAdaptiveClass = null;
        private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
        //扩展点的默认名称@SPI中的value
        private String cachedDefaultName;
        //缓存adaptive实例
        private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
        //缓存包装类型的扩展点
        private Set<Class<?>> cachedWrapperClasses;
    

    getExtensionLoader()首先从缓存中获取该扩展点的ExtensionLoader,如果缓存中不存在创建并缓存起来且key为扩展点的接口类型。

    • 执行流程分析
    -----------------------ExtensionLoader.getExtensionLoader(Class<T> type)
    ExtensionLoader.getExtensionLoader(Container.class)
      -->this.type = type;
      -->objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
         -->ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
           -->this.type = type;
           -->objectFactory =null;
    
     /**
         * @Author pengyunlong
         * @Description 静态方法,单例模式
         *              首先从缓存中获取扩展点加载器,如果缓存中不存在则创建
         * @param
         * @Date 2018/6/11 11:36
         */
        @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!");
            }
            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;
        }
    
       /**
         * @Author pengyunlong
         * @Description 构造方法
         *              扩展点加载器
         * @param
         * @Date 2018/6/7 18:38
         */
        private ExtensionLoader(Class<?> type) {
            //当前扩展点的接口类型必须,含有SPI注解
            this.type = type;
            //objectFactory IOC需要从这个变量中加载对象实例并注入
            objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
        }
    

    执行以上代码完成了2个属性的初始化

    1. 每个一个ExtensionLoader都包含了2个值: type 和 objectFactory
      Class<?> type;:要加载扩展点的接口
      ExtensionFactory objectFactory:为dubbo的IOC提供所有对象
    2. new 一个ExtensionLoader 存储在ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS

    关于这个objectFactory的一些细节:

    1. objectFactory就是通过ExtensionLoader.getExtensionLoader(ExtensionFactory.class)来实现的,它的objectFactory=null;
    2. objectFactory实际上是ExtensionFactory.class的Adaptive扩展。getAdaptiveExtension() 获取一个扩展类,如果@Adaptive注解在类上就是一个装饰类;如果注解在方法上就是一个动态代理类。而在ExtensionFactory.class的所有扩展实现中AdaptiveExtensionFactory类上有@Adaptive注解,所以ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()得到的就是AdaptiveExtensionFactory实例。AdaptiveExtensionFactory为dubbo的IOC提供所有对象,内部包含其他两个扩展点SpiExtensionFactory,SpringExtensionFactory,后面会通过它们加载spring和dubbo中的实例注入到目标对象的属性中;
      【Dubbo】Adaptive
    
    /**
     * AdaptiveExtensionFactory
     */
    @Adaptive
    public class AdaptiveExtensionFactory implements ExtensionFactory {
    
        private final List<ExtensionFactory> factories;
        /**
         * @Author pengyunlong
         * @Description 获取到其他ExtensionFactory的实现,并创建ExtensionFactory的实例
         *              SpiExtensionFactory,SpringExtensionFactory
         * @param
         * @Date 2018/6/7 17:58
         */
        public AdaptiveExtensionFactory() {
            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);
        }
        /**
         * @Author pengyunlong
         * @Description 从ExtensionFactory实例中(包括spring容器和spi),加载扩展点指定扩展点实例
         * @param
         * @Date 2018/6/7 18:01
         */
        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;
        }
    }
    
    /**
     * SpiExtensionFactory
     */
    public class SpiExtensionFactory implements ExtensionFactory {
    
        public <T> T getExtension(Class<T> type, String name) {
            if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
                ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
                if (!loader.getSupportedExtensions().isEmpty()) {
                    return loader.getAdaptiveExtension();
                }
            }
            return null;
        }
    
    }
    
    package com.alibaba.dubbo.config.spring.extension;
    /**
     * SpringExtensionFactory
     */
    public class SpringExtensionFactory implements ExtensionFactory {
    
        @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;
        }
    }
    
    

    2.加载指定的扩展点

    首先从缓存中获取,如果缓存中不存在则通过createExtension(name)创建并加入缓存

    • 代码执行流程
    -----------------------getExtension(String name)
    getExtension(String name) //指定对象缓存在cachedInstances;get出来的对象wrapper对象,例如protocol就是ProtocolFilterWrapper和ProtocolListenerWrapper其中一个。
      -->createExtension(String name)
        -->getExtensionClasses()
        -->injectExtension(T instance)//dubbo的IOC反转控制,就是从spi和spring里面提取对象赋值。
          -->objectFactory.getExtension(pt, property)
            -->SpiExtensionFactory.getExtension(type, name)
              -->ExtensionLoader.getExtensionLoader(type)
              -->loader.getAdaptiveExtension()
            -->SpringExtensionFactory.getExtension(type, name)
              -->context.getBean(name)
        -->injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))//AOP的简单设计
    
       /**
         * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
         * will be thrown.
         * 根据扩展点名称找到指定的扩展点,首先从缓存中获取,如果缓存中不存在则创建
         */
        @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;
        }
    

    创建扩展点实例

    创建扩展点实例的过程:加载字节码->创建实例-> 注入属性->包装,所以我们最后拿到的扩展点实例其实有可能是一个包装类。dubbo的IOC通过set注入完成,AOP通过wapper包装完成。

      /**
         * @Description 
         *              加载字节码
         *              创建实例
         *              注入属性
         *              包装
         */
        @SuppressWarnings("unchecked")
        private T createExtension(String name) {
            //1.获取指定扩展点的Class
            Class<?> clazz = getExtensionClasses().get(name);
            if (clazz == null) {
                throw findException(name);
            }
            try {
                T instance = (T) EXTENSION_INSTANCES.get(clazz);
                if (instance == null) {
                    //2.创建扩展点实例
                    EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
                    instance = (T) EXTENSION_INSTANCES.get(clazz);
                }
                //3.实现dubbo IOC功能,从spring和dubbo中注入属性到扩展点实例中来
                injectExtension(instance);
                Set<Class<?>> wrapperClasses = cachedWrapperClasses;
                if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                    for (Class<?> wrapperClass : wrapperClasses) {
                        //4.实现dubbo AOP功能,包装扩展点实例
                        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);
            }
        }
    
    1. 解析该扩展点的类名
      从三个路径加载spi配置的class到缓存中,META-INF/dubbo/internal/META-INF/dubbo/META-INF/services/loadfile()加载过程中还会解析class中包含的注解来填充一些缓存变量。
    变量名
    cachedAdaptiveClass 含有Adaptive注解的class
    cachedWrapperClasses 无adative注解,并且构造函数包含目标接口(type)类型的class
    cachedActivates 剩下的类含有Activate注解的class
    cachedNames 其余的class
        /**
         * @Author pengyunlong
         * @Description 如果缓存为空则加载所有的扩展class并缓存起来
         * @param
         * @Date 2018/6/7 17:08
         */
        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;
        }
    
         /**
         * @Author pengyunlong 
         * @Description 从三个路径加载spi配置的class到缓存中,cachedDefaultName为spi注解的value
         *              META-INF/dubbo/internal/ META-INF/dubbo/ META-INF/services/
         * @param   
         * @Date 2018/6/7 17:18
         */
        // synchronized in getExtensionClasses
        private Map<String, Class<?>> loadExtensionClasses() {
            final SPI defaultAnnotation = type.getAnnotation(SPI.class);
            if (defaultAnnotation != null) {
                String value = defaultAnnotation.value();
                if (value != null && (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;
        }
    

    通过把配置文件META-INF/dubbo/internal/com.alibaba.dubbo.rpc. Protocol(fileName = dir + type.getName();)的内容,存储在缓存变量里面。

    关于loadfile的一些细节

    • cachedAdaptiveClass
      如果这个class含有adative注解就赋值,例如ExtensionFactory,而例如Protocol在这个环节是没有的。
    • cachedWrapperClasses
      只有当该class无adative注解,并且构造函数包含目标接口(type)类型,例如protocol里面的spi就只有ProtocolFilterWrapper和ProtocolListenerWrapper能命中
    • cachedActivates
      剩下的类,包含Activate注解
    • cachedNames
      剩下的类就存储在这里。
     /**
         * @Author pengyunlong
         * @Description 从指定目录读取Spi配置文件,分析class并加入缓存
         *              1.cachedAdaptiveClass   含有Adaptive注解的class
         *              2.cachedWrapperClasses  含有指定参数的构造方法的class
         *              3.cachedActivates       含有Activate注解的class
         *              4.cachedNames           其余的class
         * @param
         * @Date 2018/6/7 17:13
         */
        private void loadFile(Map<String, Class<?>> extensionClasses, String dir){
            String fileName = dir + type.getName();
          //遍历SPI文件的每一行配置
         while ((line = reader.readLine()) != null) {
                  Class<?> clazz = Class.forName(line, true, classLoader);
             //扩展点实例类上有@Adaptive注解直接设置cachedAdaptiveClass
                  if (clazz.isAnnotationPresent(Adaptive.class)) {
                            if(cachedAdaptiveClass == null) {
                               cachedAdaptiveClass = clazz;
                 } else if (! cachedAdaptiveClass.equals(clazz)) {
                             throw new 
                }
                try {
                   clazz.getConstructor(type);
                   //如果扩展点实例的是含有type类型参数的构造方法则加入cachedWrapperClasses集合中
                   Set<Class<?>> wrappers = cachedWrapperClasses;
                    if (wrappers == null) {
                    cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                    wrappers = cachedWrapperClasses;
                  }
                  wrappers.add(clazz);
               } catch (NoSuchMethodException e) {
                  Activate activate = clazz.getAnnotation(Activate.class);
                  if (activate != null) {
                      cachedActivates.put(names[0], activate);
                   }
                   //其余情况则将扩展点类名加入cachedNames,key为class
                   for (String n : names) {
                        if (! cachedNames.containsKey(clazz)) {
                                   cachedNames.put(clazz, n);
                          }
                           Class<?> c = extensionClasses.get(n);
                           if (c == null) {
                                    extensionClasses.put(n, clazz);
                          } else if (c != clazz) {
                                throw new 
                            }
                      }
                }
          }
        }
    
    1. 创建实例并加入缓存

    EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());

    IOC注入

    这一步主要是实现dubbo自己的IOC,遍历扩展实例的方法查找set方法,然后从spring和dubbo容器中查找对应属性值,并反射调用set方法将属性注入到扩展点的实例中。

       /**
         * @Author pengyunlong
         * @Description 含有set开头的方法则动态属性注入
         * @param
         * @Date 2018/6/7 17:50
         */
        private T injectExtension(T instance) {
            try {
                if (objectFactory != null) {
                    for (Method method : instance.getClass().getMethods()) {
                        //寻找set方法
                        if (method.getName().startsWith("set")
                                && method.getParameterTypes().length == 1
                                && Modifier.isPublic(method.getModifiers())) {
                            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;
        }
    

    前面说到在ExtensionLoader的构造方法中objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());所以objectFactory 是ExtensionFactory的扩展点(AdaptiveExtensionFactory、SpiExtensionFactory、SpringExtensionFactory)中的一个,而AdaptiveExtensionFactory含有@Adaptive所以通过getAdaptiveExtension拿到的实际是AdaptiveExtensionFactory
    Object object = objectFactory.getExtension(pt, property); 这句代码主要就是通过SpiExtensionFactory和SpringExtensionFactory来查找property实例。AdaptiveExtensionFactory.getExtension会从SpiExtensionFactory、SpringExtensionFactory中查找指定类型或名称实现类。

    @Adaptive
    public class AdaptiveExtensionFactory implements ExtensionFactory {
    
        private final List<ExtensionFactory> factories;
        /**
         * @Author pengyunlong
         * @Description 获取到其他ExtensionFactory的实现,并创建ExtensionFactory的实例
         *              SpiExtensionFactory,SpringExtensionFactory
         * @param
         * @Date 2018/6/7 17:58
         */
        public AdaptiveExtensionFactory() {
            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);
        }
        /**
         * @Author pengyunlong
         * @Description 从ExtensionFactory实例中(包括spring容器和spi),加载扩展点指定扩展点实例
         * @param
         * @Date 2018/6/7 18:01
         */
        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;
        }
    
    }
    

    最后通过method.invoke(instance, object);实现注入,这样扩展点的实例就创建并注入完成了。

    IOC说明
    1. dubbo扩展未知类的属性来源分为:spi 和spring
      spi的扩展对象存储在SpiExtensionFactory
      spring的扩展对象存储在 SpringExtensionFactory
    2. SpringExtensionFactory的设计初衷:
      a. 设计的目的:方便开发者对扩展未知类的配置(可以用spi配置也可以spring bean实现)
      b. SpringExtensionFactory在provider发布或consumer引用一个服务的时候,会把spring的容器托付给SpringExtensionFactory中去.具体代码为:ReferenceBean.setApplicationContext 和 ServiceBean.setApplicationContext
      c. 当SpiExtensionFactory没有获取到对象的时候会遍历SpringExtensionFactory中的spring容器来获取要注入的对象。
      具体代码:AdaptiveExtensionFactory.getExtension
    public void setApplicationContext(ApplicationContext applicationContext) {
            this.applicationContext = applicationContext;
            SpringExtensionFactory.addApplicationContext(applicationContext);
        }
    
        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;
        }
    
    1. SpringExtensionFactory目前的作用?
      SpringExtensionFactory前期的设计初衷非常好,但是后来执行偏离了,没有按这个初衷去落地。因为从这SpringExtensionFactory.getExtension代码(如下:)可以看出,是从ApplicationContext获取对象的。
        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;
            }
    

    但是目前这套系统没有配置spring对象的任何痕迹;甚至连配置自定义filter类,也无法实现spring bean配置,只能spi配置。

    AOP包装

    我们在第1步加载字节码的时候已经填充了wrapperClasses这个缓存变量,而且这个缓存中的所有class均含有以扩展点接口类型为方法参数的构造方法(clazz.getConstructor(type);不会抛出异常)。遍历所有的wrapper,嵌套循环生成包装类。

      Set<Class<?>> wrapperClasses = cachedWrapperClasses;
      if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            for (Class<?> wrapperClass : wrapperClasses) {
                 //4.实现dubbo AOP功能,包装扩展点实例
                  instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
    }
    

    实际上在这里就是ProtocolFilterWrapperProtocolListenerWrapperDubboProtocol的循环包装。

    public class ProtocolFilterWrapper implements Protocol {
        private final Protocol protocol;
        public ProtocolFilterWrapper(Protocol protocol) {
            if (protocol == null) {
                throw new IllegalArgumentException("protocol == null");
            }
            this.protocol = protocol;
        }
    ...
    }
    
    public class ProtocolListenerWrapper implements Protocol {
        private final Protocol protocol;
        public ProtocolListenerWrapper(Protocol protocol) {
            if (protocol == null) {
                throw new IllegalArgumentException("protocol == null");
            }
            this.protocol = protocol;
        }
        public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
            if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
                return protocol.export(invoker);
            }
            return new ListenerExporterWrapper<T>(protocol.export(invoker), 
                    Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                            .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
        }
    ....
    }
    

    总结

    至此获取扩展点实例的过程就全部完成了


    image.png

    参考资料
    SPI
    https://blog.csdn.net/sigangjun/article/details/79071850

    相关文章

      网友评论

          本文标题:【Dubbo】SPI(2)

          本文链接:https://www.haomeiwen.com/subject/mijqsftx.html