【Dubbo】SPI(2)

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339