亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

結(jié)合Dubbo源碼分析Spi

tylin / 1877人閱讀

摘要:如前所說(shuō),的目的是獲取一個(gè)指定實(shí)現(xiàn)類(lèi)的對(duì)象。接下來(lái)從子模塊下的包的開(kāi)始分析先來(lái)看這里繼承自類(lèi)。如沒(méi)有擴(kuò)展點(diǎn),在擴(kuò)展點(diǎn)實(shí)現(xiàn)調(diào)用該方法,并返回結(jié)果。前面已經(jīng)分析過(guò),就是使用讀取文件并緩存的反轉(zhuǎn)控制,就是從和里面提取對(duì)象賦值。

如前所說(shuō),Dubbo SPI的目的是獲取一個(gè)指定實(shí)現(xiàn)類(lèi)的對(duì)象。那么Dubbo是通過(guò)什么方式獲取的呢?其實(shí)是調(diào)用ExtensionLoader.getExtension(String name)實(shí)現(xiàn)。

具體實(shí)現(xiàn)途徑有三種:
①getExtensionLoader(Class type) 為type接口new一個(gè)ExtensionLoader,然后緩存起來(lái)。
②getAdaptiveExtension() 獲取一個(gè)擴(kuò)展裝飾類(lèi)的對(duì)象,這個(gè)類(lèi)有一個(gè)規(guī)則,如果它沒(méi)有@Adaptive注解,就動(dòng)態(tài)創(chuàng)建一個(gè)裝飾類(lèi),例如Protocol$Adaptive對(duì)象。
③getExtension(String name) 獲取一個(gè)指定對(duì)象。

(1)分析ExtensionLoader.getExtensionLoader(Class type)

Dubbo的第一行代碼在哪里?

idea導(dǎo)入Dubbo源碼,在子模塊dubbo-demo-provider/src/test下有DemoProvider.java

package com.alibaba.dubbo.demo.provider;

public class DemoProvider {

    public static void main(String[] args) {
        com.alibaba.dubbo.container.Main.main(args);
    }
}

這里便是代碼的入口。
這里調(diào)到com.alibaba.dubbo.container.Main.java

package com.alibaba.dubbo.container;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.ConfigUtils;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * Main. (API, Static, ThreadSafe)
 *
 * @author william.liangf
 */
public class Main {

    public static final String CONTAINER_KEY = "dubbo.container";

    public static final String SHUTDOWN_HOOK_KEY = "dubbo.shutdown.hook";

    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    private static final ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Container.class);

    private static volatile boolean running = true;

    public static void main(String[] args) {
        try {
            if (args == null || args.length == 0) {
                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }

            final List containers = new ArrayList();
            for (int i = 0; i < args.length; i++) {
                containers.add(loader.getExtension(args[i]));
            }
            logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");

            if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
                Runtime.getRuntime().addShutdownHook(new Thread() {
                    public void run() {
                        for (Container container : containers) {
                            try {
                                container.stop();
                                logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
                            } catch (Throwable t) {
                                logger.error(t.getMessage(), t);
                            }
                            synchronized (Main.class) {
                                running = false;
                                Main.class.notify();
                            }
                        }
                    }
                });
            }

            for (Container container : containers) {
                container.start();
                logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
            }
            System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
        } catch (RuntimeException e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            System.exit(1);
        }
        synchronized (Main.class) {
            while (running) {
                try {
                    Main.class.wait();
                } catch (Throwable e) {
                }
            }
        }
    }

}

可以看到,Main類(lèi)中定義了一系列的靜態(tài)成員變量,其中:

private static final ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Container.class);

在Main類(lèi)初始化階段調(diào)用了上述第①條方式為Container創(chuàng)建擴(kuò)展點(diǎn)。
通過(guò)斷點(diǎn)跟進(jìn)getExtensionLoader方法,會(huì)進(jìn)行new ExtensionLoader(type)構(gòu)造:

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

可以看到,這里會(huì)進(jìn)一步調(diào)用getExtensionLoader方法,只是這次傳入的是ExtensionFactory.class。通過(guò)上面的代碼知道,等價(jià)于如下:

this.type = type;
objectFactory = null;

執(zhí)行以上代碼完成了2個(gè)屬性的初始化:
1.每個(gè)ExtensionLoader都包含了2個(gè)值: type 和 objectFactory
Class type;//構(gòu)造器初始化時(shí)要得到的接口名
ExtensionFactory objectFactory//構(gòu)造器初始化時(shí)設(shè)置為AdaptiveExtensionFactory,Dubbo內(nèi)部默認(rèn)的實(shí)現(xiàn)是SpiExtensionFactory和SpringExtensionFactory。
2.new 一個(gè)ExtensionLoader 存儲(chǔ)在ConcurrentMap, ExtensionLoader> EXTENSION_LOADERS里。

關(guān)于objectFactory
1.objectFactory就是ExtensionFactory,它也是通過(guò)ExtensionLoader.getExtensionLoader(ExtensionFactory.class)來(lái)實(shí)現(xiàn)的,但是它的objectFactory=null
2.objectFactory作用,它就是為dubbo的IOC提供所有對(duì)象。

(2)分析getAdaptiveExtension()
為什么要設(shè)計(jì)Adaptive?
Adaptive注解在類(lèi)和方法上有什么區(qū)別?
①注解在類(lèi)上,代表人工實(shí)現(xiàn)編碼,即實(shí)現(xiàn)了一個(gè)裝飾類(lèi),如ExtensionFactory。
②注解在方法上,代表自動(dòng)生成和編譯一個(gè)動(dòng)態(tài)的adaptive類(lèi),如Protocol$Adaptive。

接下來(lái)從子模塊dubbo-config-spring下的schema包的DubboNamespaceHandler開(kāi)始分析:

package com.alibaba.dubbo.config.spring.schema;

import com.alibaba.dubbo.common.Version;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.ModuleConfig;
import com.alibaba.dubbo.config.MonitorConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.AnnotationBean;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.config.spring.ServiceBean;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * DubboNamespaceHandler
 *
 * @author william.liangf
 * @export
 */
public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
    }
}

先來(lái)看

registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));

這里ServiceBean繼承自ServiceConfig類(lèi)。

public class ServiceConfig extends AbstractServiceConfig {

    private static final long serialVersionUID = 3033787999037024738L;

    private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
....
}

在這里通過(guò)getAdaptiveExtension()獲取protocol。

-->getAdaptiveExtension()//為cachedAdaptiveInstance賦值
  -->createAdaptiveExtension()
    -->getAdaptiveExtensionClass()//該方法看出,如果是預(yù)定義的類(lèi)就直接返回,不然動(dòng)態(tài)生成適配類(lèi)
      -->getExtensionClasses()//為cachedClasses 賦值
        -->loadExtensionClasses()
          -->loadFile(..)
      -->createAdaptiveExtensionClass()//自動(dòng)生成和編譯一個(gè)動(dòng)態(tài)的adpative類(lèi),這個(gè)類(lèi)是一個(gè)代理類(lèi)
        -->ExtensionLoader.getExtensionLoader
                  (com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
        -->compiler.compile(code, classLoader)
    -->injectExtension()//作用:進(jìn)入IOC的反轉(zhuǎn)控制模式,實(shí)現(xiàn)了動(dòng)態(tài)注入

loadFile(..)方法的作用:把SPI配置文件(如META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol)的內(nèi)容,存儲(chǔ)在緩存變量里。使用了四個(gè)緩存變量。
①緩存包含Adaptive注解的類(lèi)
cachedAdaptiveClass 如果這個(gè)Class含有adaptive注解就賦值進(jìn)去,如ExtensionFactory有,而Protocol沒(méi)有。
②緩存無(wú)Adaptive注解的封裝類(lèi)
cachedWrapperClasses 只有當(dāng)該class無(wú)adaptive注解,并且構(gòu)造方法參數(shù)為目標(biāo)接口(type,如Protocol)類(lèi)型,如Protocol里的SPI就只有ProtocolFilterWrapper和ProtocolListenerWrapper能命中,如下例:

public class ProtocolFilterWrapper implements Protocol {

    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
  。。。
}

③cachedActivates 剩下的包含Activate注解的類(lèi)
④cachedName 剩下的類(lèi)存儲(chǔ)在該map中
在loadExtensionClasses()方法中,有三處loadFile()加載SPI文件:

private Map> 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> extensionClasses = new HashMap>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

這里的三處loadFile()實(shí)際上起到真正作用的是第一個(gè):路徑為META-INF/dubbo/internal/,這個(gè)打開(kāi)dubbo.jar即可看到,這里仍然看com.alibaba.dubbo.rpc.Protocol這個(gè)SPI文件:

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
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol

上面執(zhí)行compile時(shí),框架會(huì)自動(dòng)生成如下Protocol$Adpative類(lèi)代碼:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException(
                "method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException(
                "method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public com.alibaba.dubbo.rpc.Exporter export(
            com.alibaba.dubbo.rpc.Invoker arg0)
            throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null)
            throw new IllegalArgumentException(
                    "com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException(
                    "com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url
                .getProtocol());
        if (extName == null)
            throw new IllegalStateException(
                    "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url("
                            + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader
                .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
                .getExtension(extName);
        return extension.export(arg0);//自己執(zhí)行自己,說(shuō)明當(dāng)前類(lèi)是一個(gè)代理類(lèi)
    }

    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,
            com.alibaba.dubbo.common.URL arg1)
            throws com.alibaba.dubbo.rpc.RpcException {
        if (arg1 == null)
            throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url
                .getProtocol());
        if (extName == null)
            throw new IllegalStateException(
                    "Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url("
                            + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader
                .getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
                .getExtension(extName);
        return extension.refer(arg0, arg1);//自己執(zhí)行自己,說(shuō)明當(dāng)前類(lèi)是一個(gè)代理類(lèi)
    }
}

其實(shí)就是根據(jù)如下模板生成的:

package <擴(kuò)展點(diǎn)接口所在包>;
 
public class <擴(kuò)展點(diǎn)接口名>$Adpative implements <擴(kuò)展點(diǎn)接口> {
    public <有@Adaptive注解的接口方法>(<方法參數(shù)>) {
        if(是否有URL類(lèi)型方法參數(shù)?) 使用該URL參數(shù)
        else if(是否有方法類(lèi)型上有URL屬性) 使用該URL屬性
        # 
         
        if(獲取的URL == null) {
            throw new IllegalArgumentException("url == null");
        }
 
              根據(jù)@Adaptive注解上聲明的Key的順序,從URL獲致Value,作為實(shí)際擴(kuò)展點(diǎn)名。
               如URL沒(méi)有Value,則使用缺省擴(kuò)展點(diǎn)實(shí)現(xiàn)。如沒(méi)有擴(kuò)展點(diǎn), throw new IllegalStateException("Fail to get extension");
 
               在擴(kuò)展點(diǎn)實(shí)現(xiàn)調(diào)用該方法,并返回結(jié)果。
    }
 
    public <有@Adaptive注解的接口方法>(<方法參數(shù)>) {
        throw new UnsupportedOperationException("is not adaptive method!");
    }
}

總結(jié)起來(lái),Dubbo的所有對(duì)象都是通過(guò)ExtensionLoader獲取的,SPI是內(nèi)核。

(3)分析getExtension(String name)

為了進(jìn)一步分析代理類(lèi)的擴(kuò)展類(lèi)對(duì)象生成過(guò)程,將Protocol$Adpative類(lèi)手動(dòng)創(chuàng)建到dubbo源碼子模塊dubbo-demo下的dubbo-demo-provider中,test目錄下新建包c(diǎn)om.alibaba.dubbo.rpc。然后將上述代碼拷貝其中。
然后在getExtension(extName)這里設(shè)置斷點(diǎn):

通過(guò)斷點(diǎn)跟蹤,調(diào)用鏈如下:

-->getExtension(String name) //指定對(duì)象緩存在cachedInstances;get出來(lái)的對(duì)象可能是wrapper對(duì)象,例如protocol就是ProtocolFilterWrapper和ProtocolListenerWrapper其中一個(gè)。
  -->createExtension(String name)
    -->getExtensionClasses() //前面已經(jīng)分析過(guò),就是使用loadFile讀取文件并緩存
    -->injectExtension(T instance)//dubbo的IOC反轉(zhuǎn)控制,就是從spi和spring里面提取對(duì)象賦值。
      -->objectFactory.getExtension(pt, property)//通過(guò)ExtensionFactory獲取extension,有兩種
        -->①SpiExtensionFactory.getExtension(type, name)
          -->ExtensionLoader.getExtensionLoader(type)
          -->loader.getAdaptiveExtension()
        -->②SpringExtensionFactory.getExtension(type, name)
          -->context.getBean(name)
    -->injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))//AOP的簡(jiǎn)單設(shè)計(jì),這個(gè)地方如果前面的wrapperClasses緩存不空,那么就會(huì)執(zhí)行這句代碼,如Protocol中只有Filter和Listener,通過(guò)使用ProtocolFilterWrapper或ProtocolListenerWrapper的構(gòu)造方法反射然后注入

通過(guò)上述分析,總結(jié)起來(lái)SPI getExtension()的執(zhí)行流程及設(shè)計(jì)模式如下:

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/68533.html

相關(guān)文章

  • dubbo源碼解析(二)Dubbo擴(kuò)展機(jī)制SPI

    摘要:二注解該注解為了保證在內(nèi)部調(diào)用具體實(shí)現(xiàn)的時(shí)候不是硬編碼來(lái)指定引用哪個(gè)實(shí)現(xiàn),也就是為了適配一個(gè)接口的多種實(shí)現(xiàn),這樣做符合模塊接口設(shè)計(jì)的可插拔原則,也增加了整個(gè)框架的靈活性,該注解也實(shí)現(xiàn)了擴(kuò)展點(diǎn)自動(dòng)裝配的特性。 Dubbo擴(kuò)展機(jī)制SPI 前一篇文章《dubbo源碼解析(一)Hello,Dubbo》是對(duì)dubbo整個(gè)項(xiàng)目大體的介紹,而從這篇文章開(kāi)始,我將會(huì)從源碼來(lái)解讀dubbo再各個(gè)模塊的實(shí)...

    DirtyMind 評(píng)論0 收藏0
  • dubboSPI自適應(yīng)擴(kuò)展機(jī)制

    摘要:對(duì)于這個(gè)矛盾的問(wèn)題,通過(guò)自適應(yīng)拓展機(jī)制很好的解決了。自適應(yīng)拓展機(jī)制的實(shí)現(xiàn)邏輯比較復(fù)雜,首先會(huì)為拓展接口生成具有代理功能的代碼。 1、背景 在 Dubbo 中,很多拓展都是通過(guò) SPI 機(jī)制進(jìn)行加載的,比如 Protocol、Cluster、LoadBalance 等。有時(shí),有些拓展并不想在框架啟動(dòng)階段被加載,而是希望在拓展方法被調(diào)用時(shí),根據(jù)運(yùn)行時(shí)參數(shù)進(jìn)行加載。這聽(tīng)起來(lái)有些矛盾。拓展未被...

    vvpale 評(píng)論0 收藏0
  • 聊聊Dubbo - Dubbo可擴(kuò)展機(jī)制實(shí)戰(zhàn)

    摘要:今天我想聊聊的另一個(gè)很棒的特性就是它的可擴(kuò)展性。的擴(kuò)展機(jī)制在的官網(wǎng)上,描述自己是一個(gè)高性能的框架。接下來(lái)的章節(jié)中我們會(huì)慢慢揭開(kāi)擴(kuò)展機(jī)制的神秘面紗。擴(kuò)展擴(kuò)展點(diǎn)的實(shí)現(xiàn)類(lèi)。的定義在配置文件中可以看到文件中定義了個(gè)的擴(kuò)展實(shí)現(xiàn)。 摘要: 在Dubbo的官網(wǎng)上,Dubbo描述自己是一個(gè)高性能的RPC框架。今天我想聊聊Dubbo的另一個(gè)很棒的特性, 就是它的可擴(kuò)展性。 Dubbo的擴(kuò)展機(jī)制 在Dub...

    techstay 評(píng)論0 收藏0
  • dubboSPI

    摘要:簡(jiǎn)介全稱為,是一種服務(wù)發(fā)現(xiàn)機(jī)制。的本質(zhì)是將接口實(shí)現(xiàn)類(lèi)的全限定名配置在文件中,并由服務(wù)加載器讀取配置文件,加載實(shí)現(xiàn)類(lèi)。不過(guò),并未使用原生的機(jī)制,而是對(duì)其進(jìn)行了增強(qiáng),使其能夠更好的滿足需求。并未使用,而是重新實(shí)現(xiàn)了一套功能更強(qiáng)的機(jī)制。 1、SPI簡(jiǎn)介 SPI 全稱為 Service Provider Interface,是一種服務(wù)發(fā)現(xiàn)機(jī)制。SPI 的本質(zhì)是將接口實(shí)現(xiàn)類(lèi)的全限定名配置在文件中...

    UnixAgain 評(píng)論0 收藏0
  • 聽(tīng)過(guò)了API咱們來(lái)看看SPI是什么

    摘要:引語(yǔ)平時(shí)倒是聽(tīng)得很多又是啥別急我們來(lái)先看看面向接口編程的調(diào)用關(guān)系,來(lái)了解一下,和的相似和不同之處。理解先來(lái)一段官話的介紹全稱為,是內(nèi)置的一種服務(wù)提供發(fā)現(xiàn)機(jī)制聽(tīng)了一臉懵逼好的,我們結(jié)合圖片來(lái)理解一下。然后調(diào)用了內(nèi)部類(lèi)的方法。 引語(yǔ) 平時(shí)API倒是聽(tīng)得很多?SPI又是啥.別急我們來(lái)先看看面向接口編程的調(diào)用關(guān)系,來(lái)了解一下,API和SPI的相似和不同之處。 SPI理解 先來(lái)一段官話的介紹:S...

    wapeyang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<