【Dubbo】SPI(2)

dubbo自定義SPI

為什么dubbo要自己設(shè)計一套SPI脖含?

  1. JDK標(biāo)準(zhǔn)的SPI會一次性實例化擴展點所有實現(xiàn)比规,如果有擴展實現(xiàn)初始化很耗時守屉,而且沒用上也加載夷野,會很浪費資源;
  2. 增加了對擴展點IOC和AOP的支持荣倾,一個擴展點可以直接setter注入其它擴展點悯搔。
  • 這是原始JDK spi的代碼
  ServiceLoader<Command> serviceLoader=ServiceLoader.load(Command.class); 
  for(Command command:serviceLoader){  
      command.execute();  
  }  

dubbo在原來的基礎(chǔ)上設(shè)計了以下功能:

  1. 原始JDK spi不支持緩存;dubbo設(shè)計了緩存對象:spi的key與value 緩存在 cachedInstances對象里面舌仍,它是一個ConcurrentMap妒貌;
  2. 原始JDK spi不支持默認值,dubbo設(shè)計默認值:@SPI("dubbo") 代表默認的spi對象铸豁。例如:
    Protocol的@SPI("dubbo")就是 DubboProtocol,通過 ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension()拿默認對象
  3. jdk要用for循環(huán)判斷對象灌曙,dubbo設(shè)計getExtension靈活方便,動態(tài)獲取spi對象节芥。例如: ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi的key)來提取對象;
  4. 原始JDK spi不支持 AOP功能在刺,dubbo設(shè)計增加了AOP功能,在cachedWrapperClasses,在原始spi類头镊,包裝了XxxxFilterWrapper XxxxListenerWrapper蚣驼;
  5. 原始JDK spi不支持 IOC功能,dubbo設(shè)計增加了IOC,通過set注入:injectExtension(T instance)相艇。

Dubbo的SPI約定

擴展點的配置都放到\META-INF\dubbo\internal路徑下隙姿,且文件名為擴展接口的包名+接口名;文件內(nèi)容為:擴展名=實現(xiàn)類的包名+類名厂捞,例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為了實現(xiàn)IOC和AOP输玷,設(shè)計了多種功能不同的擴展點。我們可以根據(jù)擴展點功能的不同把擴展點大致分成4類:

擴展點類型 說明
Adaptive 含有Adaptive注解的擴展點
Wrapper 含有構(gòu)造函數(shù)的參數(shù)為擴展點接口類型的擴展點
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

我們可以按照上面的規(guī)則將所有的Protocol擴展點分類:

擴展點類型 名稱
Adaptive Protocol$Adaptive(動態(tài)編譯)
Wrapper filter靡馁,listener
其他 injvm欲鹏,registry,dubbo臭墨,mock

通過ExtensionLoader.getExtensionLoader(Protpcol.class).getAdaptiveExtension()創(chuàng)建Protpcol的默認擴展點“dubbo”赔嚎,而如果我們調(diào)用得到的這個實例的方法,它的實際調(diào)用流程卻是
Protocol$Adaptive->ProtocolFilterWrapper->ProtocolListenerWrapper->DubboProtocol
似乎跟Spring的AOP很像胧弛,跟一下源碼尤误,來一探究竟。

源碼分析

  • dubbo spi 的目的:獲取一個指定實現(xiàn)類的對象结缚。
  • 途徑:ExtensionLoader.getExtension(String name)
  • 實現(xiàn)路徑:
    getExtensionLoader(Class<T> type)就是為該接口new 一個ExtensionLoader损晤,然后緩存起來。
    getAdaptiveExtension() 獲取一個擴展類红竭,如果@Adaptive注解在類上就是一個裝飾類尤勋;如果注解在方法上就是一個動態(tài)代理類喘落,例如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. 獲取擴展點加載器

首先認識一下擴展點加載器赌朋,他的靜態(tài)變量是一些全局的緩存,私有變量會在第一次獲取該擴展點實例的時候初始化

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

    // ==============================
    /*****當(dāng)前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箕慧,如果緩存中不存在創(chuàng)建并緩存起來且key為擴展點的接口類型。

  • 執(zhí)行流程分析
-----------------------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 靜態(tài)方法茴恰,單例模式
     *              首先從緩存中獲取擴展點加載器颠焦,如果緩存中不存在則創(chuàng)建
     * @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) {
            //緩存中部存在則創(chuàng)建
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }
   /**
     * @Author pengyunlong
     * @Description 構(gòu)造方法
     *              擴展點加載器
     * @param
     * @Date 2018/6/7 18:38
     */
    private ExtensionLoader(Class<?> type) {
        //當(dāng)前擴展點的接口類型必須,含有SPI注解
        this.type = type;
        //objectFactory IOC需要從這個變量中加載對象實例并注入
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

執(zhí)行以上代碼完成了2個屬性的初始化

  1. 每個一個ExtensionLoader都包含了2個值: type 和 objectFactory
    Class<?> type;:要加載擴展點的接口
    ExtensionFactory objectFactory:為dubbo的IOC提供所有對象
  2. new 一個ExtensionLoader 存儲在ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS

關(guān)于這個objectFactory的一些細節(jié):

  1. objectFactory就是通過ExtensionLoader.getExtensionLoader(ExtensionFactory.class)來實現(xiàn)的往枣,它的objectFactory=null伐庭;
  2. objectFactory實際上是ExtensionFactory.class的Adaptive擴展。getAdaptiveExtension() 獲取一個擴展類分冈,如果@Adaptive注解在類上就是一個裝飾類圾另;如果注解在方法上就是一個動態(tài)代理類。而在ExtensionFactory.class的所有擴展實現(xiàn)中AdaptiveExtensionFactory類上有@Adaptive注解雕沉,所以ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()得到的就是AdaptiveExtensionFactory實例集乔。AdaptiveExtensionFactory為dubbo的IOC提供所有對象,內(nèi)部包含其他兩個擴展點SpiExtensionFactory,SpringExtensionFactory坡椒,后面會通過它們加載spring和dubbo中的實例注入到目標(biāo)對象的屬性中扰路;
    【Dubbo】Adaptive

/**
 * AdaptiveExtensionFactory
 */
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;
    /**
     * @Author pengyunlong
     * @Description 獲取到其他ExtensionFactory的實現(xiàn),并創(chuàng)建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)創(chuàng)建并加入緩存

  • 代碼執(zhí)行流程
-----------------------getExtension(String name)
getExtension(String name) //指定對象緩存在cachedInstances丈攒;get出來的對象wrapper對象哩罪,例如protocol就是ProtocolFilterWrapper和ProtocolListenerWrapper其中一個。
  -->createExtension(String name)
    -->getExtensionClasses()
    -->injectExtension(T instance)//dubbo的IOC反轉(zhuǎn)控制巡验,就是從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的簡單設(shè)計
   /**
     * Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
     * will be thrown.
     * 根據(jù)擴展點名稱找到指定的擴展點,首先從緩存中獲取显设,如果緩存中不存在則創(chuàng)建
     */
    @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) {
                    //創(chuàng)建擴展點實例
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

創(chuàng)建擴展點實例

創(chuàng)建擴展點實例的過程:加載字節(jié)碼->創(chuàng)建實例-> 注入屬性->包裝框弛,所以我們最后拿到的擴展點實例其實有可能是一個包裝類。dubbo的IOC通過set注入完成敷硅,AOP通過wapper包裝完成功咒。

  /**
     * @Description 
     *              加載字節(jié)碼
     *              創(chuàng)建實例
     *              注入屬性
     *              包裝
     */
    @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.創(chuàng)建擴展點實例
                EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //3.實現(xiàn)dubbo IOC功能愉阎,從spring和dubbo中注入屬性到擴展點實例中來
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    //4.實現(xiàn)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注解,并且構(gòu)造函數(shù)包含目標(biāo)接口(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();)的內(nèi)容猿挚,存儲在緩存變量里面。

關(guān)于loadfile的一些細節(jié)

  • cachedAdaptiveClass
    如果這個class含有adative注解就賦值驶鹉,例如ExtensionFactory绩蜻,而例如Protocol在這個環(huán)節(jié)是沒有的。
  • cachedWrapperClasses
    只有當(dāng)該class無adative注解室埋,并且構(gòu)造函數(shù)包含目標(biāo)接口(type)類型办绝,例如protocol里面的spi就只有ProtocolFilterWrapper和ProtocolListenerWrapper能命中
  • cachedActivates
    剩下的類,包含Activate注解
  • cachedNames
    剩下的類就存儲在這里姚淆。
 /**
     * @Author pengyunlong
     * @Description 從指定目錄讀取Spi配置文件孕蝉,分析class并加入緩存
     *              1.cachedAdaptiveClass   含有Adaptive注解的class
     *              2.cachedWrapperClasses  含有指定參數(shù)的構(gòu)造方法的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注解直接設(shè)置cachedAdaptiveClass
              if (clazz.isAnnotationPresent(Adaptive.class)) {
                        if(cachedAdaptiveClass == null) {
                           cachedAdaptiveClass = clazz;
             } else if (! cachedAdaptiveClass.equals(clazz)) {
                         throw new 
            }
            try {
               clazz.getConstructor(type);
               //如果擴展點實例的是含有type類型參數(shù)的構(gòu)造方法則加入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. 創(chuàng)建實例并加入緩存

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

IOC注入

這一步主要是實現(xiàn)dubbo自己的IOC腌逢,遍歷擴展實例的方法查找set方法降淮,然后從spring和dubbo容器中查找對應(yīng)屬性值,并反射調(diào)用set方法將屬性注入到擴展點的實例中搏讶。

   /**
     * @Author pengyunlong
     * @Description 含有set開頭的方法則動態(tài)屬性注入
     * @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的構(gòu)造方法中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中查找指定類型或名稱實現(xiàn)類淑玫。

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;
    /**
     * @Author pengyunlong
     * @Description 獲取到其他ExtensionFactory的實現(xiàn),并創(chuàng)建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);實現(xiàn)注入,這樣擴展點的實例就創(chuàng)建并注入完成了。

IOC說明
  1. dubbo擴展未知類的屬性來源分為:spi 和spring
    spi的擴展對象存儲在SpiExtensionFactory
    spring的擴展對象存儲在 SpringExtensionFactory
  2. SpringExtensionFactory的設(shè)計初衷:
    a. 設(shè)計的目的:方便開發(fā)者對擴展未知類的配置(可以用spi配置也可以spring bean實現(xiàn))
    b. SpringExtensionFactory在provider發(fā)布或consumer引用一個服務(wù)的時候叁鉴,會把spring的容器托付給SpringExtensionFactory中去.具體代碼為:ReferenceBean.setApplicationContext 和 ServiceBean.setApplicationContext
    c. 當(dāng)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前期的設(shè)計初衷非常好幌墓,但是后來執(zhí)行偏離了但壮,沒有按這個初衷去落地冀泻。因為從這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;
        }

但是目前這套系統(tǒng)沒有配置spring對象的任何痕跡蜡饵;甚至連配置自定義filter類弹渔,也無法實現(xiàn)spring bean配置,只能spi配置溯祸。

AOP包裝

我們在第1步加載字節(jié)碼的時候已經(jīng)填充了wrapperClasses這個緩存變量肢专,而且這個緩存中的所有class均含有以擴展點接口類型為方法參數(shù)的構(gòu)造方法(clazz.getConstructor(type);不會拋出異常)。遍歷所有的wrapper,嵌套循環(huán)生成包裝類焦辅。

  Set<Class<?>> wrapperClasses = cachedWrapperClasses;
  if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
        for (Class<?> wrapperClass : wrapperClasses) {
             //4.實現(xiàn)dubbo AOP功能,包裝擴展點實例
              instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
        }
}

實際上在這里就是ProtocolFilterWrapperProtocolListenerWrapperDubboProtocol的循環(huán)包裝博杖。

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

總結(jié)

至此獲取擴展點實例的過程就全部完成了


image.png

參考資料
SPI
https://blog.csdn.net/sigangjun/article/details/79071850

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市筷登,隨后出現(xiàn)的幾起案子剃根,更是在濱河造成了極大的恐慌,老刑警劉巖前方,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狈醉,死亡現(xiàn)場離奇詭異,居然都是意外死亡镣丑,警方通過查閱死者的電腦和手機舔糖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來莺匠,“玉大人金吗,你說我怎么就攤上這事∪たⅲ” “怎么了摇庙?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長遥缕。 經(jīng)常有香客問我卫袒,道長,這世上最難降的妖魔是什么单匣? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任夕凝,我火速辦了婚禮,結(jié)果婚禮上户秤,老公的妹妹穿的比我還像新娘码秉。我一直安慰自己,他們只是感情好鸡号,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布转砖。 她就那樣靜靜地躺著,像睡著了一般鲸伴。 火紅的嫁衣襯著肌膚如雪府蔗。 梳的紋絲不亂的頭發(fā)上晋控,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機與錄音姓赤,去河邊找鬼赡译。 笑死,一個胖子當(dāng)著我的面吹牛模捂,可吹牛的內(nèi)容都是我干的捶朵。 我是一名探鬼主播蜘矢,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狂男,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了品腹?” 一聲冷哼從身側(cè)響起岖食,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舞吭,沒想到半個月后泡垃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡羡鸥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年蔑穴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惧浴。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡存和,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衷旅,到底是詐尸還是另有隱情捐腿,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布柿顶,位于F島的核電站茄袖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嘁锯。R本人自食惡果不足惜宪祥,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望家乘。 院中可真熱鬧蝗羊,春花似錦、人聲如沸烤低。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扑馁。三九已至涯呻,卻和暖如春凉驻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背复罐。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工涝登, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人效诅。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓胀滚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乱投。 傳聞我的和親對象是個殘疾皇子咽笼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355