DUBBO SPI实现核心-ExtensionLoader

概念

dubbo spi是dubbo对JDK spi的升级,针对JDK spi的一些弱势进行了优化,官网的介绍如下:

Dubbo 的扩展点加载从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。

Dubbo 改进了 JDK 标准的 SPI 的以下问题:

JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本>时,会报不支持 ruby,而不是真正失败的原因。
增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

附上链接:https://dubbo.incubator.apache.org/zh-cn/docs/dev/SPI.html

dubbo一方面用spi机制完成自己的默认实现,同时允许开发者在不清楚dubbo代码细节的同时根据自己需求对框架能力进行拓展。模仿该设计方式,可以让很多流程性业务编码变得非常简洁。

环境搭建

从来不想在搭环境上浪费太多时间,用最简单的方式,免得降低读代码的耐心,
不需要构建spring应用,普通工程即可,在pom中添加依赖:

<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.6.6</version>
    </dependency>

    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.36.Final</version>
    </dependency>

    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>3.0.1</version>
    </dependency>

dubbo版本使用2.6.6,后面源码介绍也是基于这个版本的,使用redis注册中心。
接口以及其实现类:

public interface DemoService {
    String sayHello(String name);
}

public class DemoServiceImpl  implements DemoService{
    public String sayHello(String name) {
        return "Hello " + name;
    }
}

参考官方文档,编写测试代码:

// 服务实现
DemoService demoService = new DemoServiceImpl();

// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("dubbo-learn");

// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setProtocol("redis");
registry.setAddress("127.0.0.1:6379");

// 服务提供者协议配置
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(12345);
protocol.setThreads(200);

// 服务提供者暴露服务配置
// 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
ServiceConfig<DemoService> service = new ServiceConfig<DemoService>();
service.setApplication(application);

// 多个注册中心可以用setRegistries()
        service.setRegistry(registry);

        // 多个协议可以用setProtocols()
        service.setProtocol(protocol);
        service.setInterface(DemoService.class);
        service.setRef(demoService);
        service.setVersion("1.0.0");
        // service.setFilter("demoFilter");

        // 暴露及注册服务
        service.export();

        // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
        ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
        reference.setApplication(application);
        // 多个注册中心可以用setRegistries()
        reference.setRegistry(registry);
        reference.setInterface(DemoService.class);
        reference.setVersion("1.0.0");

        // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用
        DemoService demoService1 = reference.get();
        System.out.println(demoService1.sayHello("jules"));

启动redis,就可以执行测试代码了,效果如图


image.png

我们顺便看下服务暴露以后,redis中保存的服务信息


image.png

可以看到注册信息在redis是以哈希表的方式保存,哈希表的key即服务URL信息

源码

初始化

在阅读dubbo代码时,经常看到如下实现:

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

dubbo使用ExtensionLoader实现他的spi机制,以上代码会生成一个Protocol的动态代理实现,由代理去执行真实逻辑。

每个扩展点对应一个ExtensionLoader,getExtensionLoader(Protocol.class)实际为获取Protocol.class的ExtensionLoader,代码如下:

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

先从缓存中获取,获取不到则创建并放入缓存中,以供下次使用,ExtensionLoader只有一个构造方法:

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

获取ExtensionLoader以后,执行getAdaptiveExtension()方法获取动态生成的Protocol,我们阅读getAdaptiveExtension的代码逻辑:

    public T getAdaptiveExtension() {
        // 从缓存中读取
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            // 检查生成的时候是否出现异常
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    // 典型的单例生成方式,两次验证
                    if (instance == null) {
                        try {
                            // 生成单例并且缓存起来
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }
        return (T) instance;
    }

以上代码可知,核心逻辑在createAdaptiveExtension中,进一步阅读:

    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

代理类在getAdaptiveExtensionClass中生成,然后newInstance生成对象注入到injectExtension方法,于是先阅读Class的创建过程,getAdaptiveExtensionClass的代码为:

    private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

getExtensionClasses()篇幅比较长,主要是在第一次启用的时候,加载所有的扩展信息,getExtensionClasses调用loadExtensionClasses,我们直接看loadExtensionClasses的代码:

    private Map<String, Class<?>> loadExtensionClasses() {
        // 读取扩展点上的value值,放入cachedDefaultName中
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((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<?>>();
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadDirectory(extensionClasses, DUBBO_DIRECTORY);
        loadDirectory(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

dubbo的spi注解有一个String的value对象,以上代码会读取value值并存入cachedDefaultName中,可以看下Protocol的spi注解value给的值是dubbo,申明了默认的实现


image.png

然后,连续调用了三个loadDirectory方法,三个DIRECTORY分别是:

  • META-INF/dubbo/internal/
  • META-INF/dubbo/
  • META-INF/services/
    其中META-INF/dubbo/internal/里面都是dubbo内部的一些扩展实现,官网在介绍spi机制的时候约定的路径是META-INF/dubbo/,理论上META-INF/services/也支持。
    打开/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件,里面有dubbo实现的对扩展点Protocol的实现:
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
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
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper

Protocol的默认实现类是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol。

loadDirectory调用loadResource,读取/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件并且一行一行遍历,获取实现类的class对象,然后在loadResource中会调用loadClass方法:

    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        // 处理Adaptive注解并且放到cachedAdaptiveClass中
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            if (cachedAdaptiveClass == null) {
                cachedAdaptiveClass = clazz;
            } else if (!cachedAdaptiveClass.equals(clazz)) {
                throw new IllegalStateException("More than 1 adaptive class found: "
                        + cachedAdaptiveClass.getClass().getName()
                        + ", " + clazz.getClass().getName());
            }
        // 包装类,则放入wrappers中
        } else if (isWrapperClass(clazz)) {
            Set<Class<?>> wrappers = cachedWrapperClasses;
            if (wrappers == null) {
                cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                wrappers = cachedWrapperClasses;
            }
            wrappers.add(clazz);
        } else {
            // 未带注解的实现类,放入extensionClasses和cachedNames中
            clazz.getConstructor();
            String[] names = NAME_SEPARATOR.split(name);
            if (names != null && names.length > 0) {
                Activate activate = clazz.getAnnotation(Activate.class);
                if (activate != null) {
                    cachedActivates.put(names[0], activate);
                }
                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 IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                    }
                }
            }
        }
    }

这里引出了两个个非常重要的注解:Adaptive和Activate,具体作用我们后面再说,阅读上面代码的第一个if可以确认,dubbo只允许一个实现类上出现Adaptive注解,否则会出现IllegalStateException异常。
总结一下上面的代码,遍历/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件,获取到内部所有的class对象,会遇到四种情况:

类别 处理 备注
实现类并且带了Adaptive注解 放到cachedAdaptiveClass 只允许有一个带有该注解
实现类并且不带Adaptive注解 放到cachedActivates和cachedNames -
实现类并且带Activate注解和Adaptive注解 放到cachedActivates和cachedNames的同时,放入cachedActivates -
包装类 放到cachedWrapperClasses -

我们现在回到getAdaptiveExtensionClass方法,避免去翻,在附上一次代码:

    private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

通过阅读,已经了解getExtensionClasses方法主要是对扩展文件的解析和实现类的加载,其实也是为我们后续的调用做好准备,加载需要做IO,好在每个ExtensionLoader只需要执行一次具体的加载逻辑就行,初始化的时候已经准备就绪了。

接下来也是核心部分,也就是createAdaptiveExtensionClass方法,真正去创建Protocol代理实现类的方法:

    private Class<?> createAdaptiveExtensionClass() {
        String code = createAdaptiveExtensionClassCode();
        ClassLoader classLoader = findClassLoader();
        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader
         .getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class)
         .getAdaptiveExtension();
        return compiler.compile(code, classLoader);
    }

第一行调用createAdaptiveExtensionClassCode生成code,createAdaptiveExtensionClassCode方法篇幅很长,不再赘述了,功能是生成动态类的类编码,Protocol会生成如下代码:

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive 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.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);
        }
    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);
    }
}

可以看到,代理类实现扩展点Protocol的所有实现,其中对export和refer进行了实现,而getDefaultPort和destroy会直接异常,原因是Protocol的export和refer方法带有注解@Adaptive,在createAdaptiveExtensionClassCode的逻辑中,只对有@Adaptive修饰的方法进行实现,@Adaptive可以注解在方法或者类上,注解在方法上则会动态生成实现方法,在该方法中会去查找真实需要执行逻辑的实现类(dubbo自带的或者是开发者扩展的)。
在Protocol$Adaptive中通过

com.alibaba.dubbo.rpc.Protocol extension ExtensionLoader.getExtensionLoader(
                com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

获取到一个Protocol的实现类,然后调用这个实现类对应的方法,那么我们可以判断,只有在发生真实调用的时候,我们才可以判断具体是执行了哪个实现类的对应方法。

最后,用对应的classLoader实例化Protocol$Adaptive,初始化过程就完成了。

调用

接下来看下怎么使用这个代理类吧,在Dubbo官网提供的分层模型中,Protocol位于远程调用层,主要封装了RPC调用,其他几个很重要的概念:Invoker,Invoker也在这一层,劝退重灾区。我们以protocol的refer进行探索,refer方法会生成Invoker,基于不同的协议会生成不同的Invoker。
可以直接在com.alibaba.dubbo.config.ReferenceConfig#createProxy中进行断点,Proxy初始化需要Invoker,Invoker的生成逻辑入口在createProxy中。
截取createProxy中的部分代码

        if (isJvmRefer) {
            URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
            invoker = refprotocol.refer(interfaceClass, url);
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + interfaceClass.getName());
            }
        } else {
            if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
                String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
                if (us != null && us.length > 0) {
                    for (String u : us) {
                        URL url = URL.valueOf(u);
                        if (url.getPath() == null || url.getPath().length() == 0) {
                            url = url.setPath(interfaceName);
                        }
                        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                            urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else { // assemble URL from register center's configuration
                List<URL> us = loadRegistries(false);
                if (us != null && !us.isEmpty()) {
                    for (URL u : us) {
                        URL monitorUrl = loadMonitor(u);
                        if (monitorUrl != null) {
                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                        urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    }
                }
                if (urls.isEmpty()) {
                    throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
                }
            }

            if (urls.size() == 1) {
                invoker = refprotocol.refer(interfaceClass, urls.get(0));
            } else {
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {
                    invokers.add(refprotocol.refer(interfaceClass, url));
                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // use last registry url
                    }
                }
                if (registryURL != null) { // registry url is available
                    // use AvailableCluster only when register's cluster is available
                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                } else { // not a registry url
                    invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }

这边啰嗦下,目前的版本中,本地服务默认就是本地调用了,也就是如果你的服务提供者和你的服务发起者都在本地,默认是使用本地服务,以上代码中isJvmRefer会等于true,我在自己阅读的时候,一般直接回在debug的时候去修改isJvmRefer的值以生成远程调用的Invoker,这次的关注点不在Invoker,我们关心的是refprotocol.refer(interfaceClass, url)在执行的时候,是怎么找到对应的实现类的。
由之前的分析得知,这边的refprotocol.refer其实是调用了Protocol$Adaptive.refer方法(我一直不知道怎么对动态生成的类进行debug,是不是完全不行呢?),代码是:

    public Invoker refer(Class arg0, URL arg1) throws RpcException {
            if (arg1 == null) throw new IllegalArgumentException("url == null");
                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])");
            Protocol extension = (Protocol) ExtensionLoader.getExtensionLoader(
                Protocol.class).getExtension(extName);
            return extension.refer(arg0, arg1);
        }

这边有两行重要的代码

String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);

根据URL信息(dubbo的协议都是存在url里面的)信息,获取到Protocol,如果是null,就默认使用dubbo,否则使用URL的配置,翻回去看下META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件的内容,每个extName其实就是对应的Protocol的实现。
我们回到ExtensionLoader,开始阅读getExtension的代码

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

cachedInstances用来缓存对应的实现,第一次进来肯定是空的,dubbo只有在真正需要某个实现类的时候才去实例化它,所以不需要担心会有不使用但是很耗时的类被实例化。然后执行createExtension(name)实例化扩展对象。

    private T createExtension(String name) {
        // 从缓存中拿到对应的Class,在加载配置文件的时候已经加入缓存
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

实例化或者从缓存中拿到对应的实例,injectExtension会进行简单的依赖注入,然后返回实例,这时我们返回的参是真实要去执行refer方法的Protocol实现,本地服务就是InjvmProtocol。

至此,ExtensionLoader初始化 到执行这个流程就结束了。


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

推荐阅读更多精彩内容