Dubbo的SPI机制

一言不合直接上代码

首先引入dubbo的包

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-common</artifactId>
            <version>2.7.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-rpc-http</artifactId>
            <version>2.7.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-rpc-dubbo</artifactId>
            <version>2.7.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-rpc-api</artifactId>
            <version>2.7.5</version>
        </dependency>
    </dependencies>

接下来就是使用Dubbo的SPI机制来实现动态加载。

与Java的API创建类似 

文件中的内容就是对应的实现类

black=com.csf.dubbo.BlackCar
red=com.csf.dubbo.BlackCar

 

 创建一个接口和不同的实现类

package com.csf.dubbo;

import org.apache.dubbo.common.extension.SPI;

@SPI
public interface Car {
    String getName();
}
package com.csf.dubbo;

public class RedCar implements Car {
    @Override
    public String getName() {
        return "red";
    }
}
package com.csf.dubbo;

public class BlackCar implements Car{
    @Override
    public String getName() {
        return "black";
    }
}

接下来就是创建一个main方法进行调用。

public static void main(String[] args) {
        ExtensionLoader<Car> extensionLoader = ExtensionLoader.getExtensionLoader(Car.class);
        Car black = extensionLoader.getExtension("black");
        System.out.println(black.getName());
    }

执行main方法的运行结果,执行了BlackCar的getName

以上Dubbo提供的基本实现。

如果有包装类Wrapper的类,那么在获取black的时候会先获取到包装类,执行包装类的方法。

package com.csf.dubbo;

public class WrapperCar implements Car {

    private Car car;

    //接口的扩展类还是包装类主要就是看有没有这个方法
    public WrapperCar(Car car) {
        this.car = car;
    }

    @Override
    public String getName() {
        return "wrapper";
    }
}

 

Dubbo中SPI的代理机制

紧接着继续创建Person的接口和实现以及配置文件。

black=com.csf.dubbo.BlackPerson
package com.csf.dubbo;

import org.apache.dubbo.common.extension.SPI;

@SPI
public interface Person {
    Car getCar();
}
package com.csf.dubbo;

public class BlackPerson implements Person {

    private Car car;

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public Car getCar() {
        return car;
    }
}

package com.csf.dubbo;

import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.rpc.Protocol;

public class Test {
    public static void main(String[] args) {


        ExtensionLoader<Person> extensionLoader1 = ExtensionLoader.getExtensionLoader(Person.class);
        Person black1 = extensionLoader1.getExtension("black");
        System.out.println(black1.getCar().getName());


    }
}

ok,很顺利的把写完了,这时候执行结果报错,空指针异常:

这是为什么呢,因为BlackPerson中引入了Car接口,这时候,在实例化BlackPerson对象的时候,需要进行依赖注入,但是此时我们并未指定具体的Car,所以这里Dubbo做了一个处理。这里会生成一个Adaptive代理对象,通过URL来指定具体要实例化的对象。

 

 

接下来就是重点了。通过源码解析来说说这个问题

为什么会报空指针异常,这就需要我们来分析一下这几行代码到底做了什么事情

先从第一行开始。

 

 这个很清晰,根据传入的type类型做一些校验,非空,是否为接口,是否有SPI的注解,当然这些个东西都ok。

紧接着就是从 EXTENSION_LOADERS 这个属性中取值,这是一个map对象。里面放的是extensionLoader对象。

 这里能看到的就是从EXTENSION_LOADERS这个map里面获取extensionLoader对象,没有获取到就创建一个,放进去。这里看完了,没有问题。

那么重点来了:

Person black1 = extensionLoader1.getExtension("black");

这里到底干了什么事情呢?

 

if ("true".equals(name)) {
    return getDefaultExtension();
}

 

 

 

这行代码是如果传的true,就取默认的实现类, 

 

 这里就是把对应目录下面接口文件中的实现类都加载了

private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst) {
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls = null;
            ClassLoader classLoader = findClassLoader();
            
            // try to load from ExtensionLoader's ClassLoader first
            if (extensionLoaderClassLoaderFirst) {
                ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
                if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                    urls = extensionLoaderClassLoader.getResources(fileName);
                }
            }
            
            if(urls == null || !urls.hasMoreElements()) {
                if (classLoader != null) {
                    urls = classLoader.getResources(fileName);
                } else {
                    urls = ClassLoader.getSystemResources(fileName);
                }
            }

            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

 这里主要就是解析文件,将文件中的内容解析了,这种key=value的形式就是在这里解析的。

这里有个重点就是将Adaptive,Wrapper,当前类等缓存起来。

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
        if (!type.isAssignableFrom(clazz)) {
            throw new IllegalStateException("Error occurred when loading extension class (interface: " +
                    type + ", class line: " + clazz.getName() + "), class "
                    + clazz.getName() + " is not subtype of interface.");
        }
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz);
        } else if (isWrapperClass(clazz)) {
            cacheWrapperClass(clazz);
        } else {
            clazz.getConstructor();
            if (StringUtils.isEmpty(name)) {
                name = findAnnotationName(clazz);
                if (name.length() == 0) {
                    throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
                }
            }

            String[] names = NAME_SEPARATOR.split(name);
            if (ArrayUtils.isNotEmpty(names)) {
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    saveInExtensionClass(extensionClasses, clazz, n);
                }
            }
        }
    }

回到原来的地方。接着走

final Holder<Object> holder = getOrCreateHolder(name);

这里为什么要创建一个holder<Object> 呢。   这是为了处理并发问题。

当流程走到这里的时候还没有创建对应的对象,也就是说对象还没有进行实例化。而下面又是一个经典对象穿件双重非空判断。需要一把锁。这里就是将对应的对象的名字创建一个holder对象,来进行加锁。

final Holder<Object> holder = getOrCreateHolder(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;

 

后面把流程图加上。更加清晰一点。看源码需要方式。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值