dubbo spi源码详解

1 jdk spi

spi全名Service Provider Interface,是一种服务发现机制,即为接口寻找服务实现的机制。
jdk spi 约定提供方只需制定接口规范,提供给外部实现,通过读取jar包META-INF/services/目录以接口名命令的文件来加载该接口的实现,文件里就是该接口的具体实现。
下面是一个简单的例子:
在这里插入图片描述
提供一个接口JdkSpiService

public interface JdkSpiService {
    
    

    public void hello();
}

提供两个接口实现类JdkSpiServiceImpl1,JdkSpiServiceImpl2

public class JdkSpiServiceImpl1 implements JdkSpiService {
    
    
    @Override
    public void hello() {
    
    
        System.out.println("hello jdk SPI 1");
    }
}
public class JdkSpiServiceImpl2 implements JdkSpiService {
    
    
    @Override
    public void hello() {
    
    
        System.out.println("hello jdk SPI 2");
    }
}

在resource目录下创建com.example.demo.jdkspi.service.JdkSpiService文件内容如下

com.example.demo.jdkspi.service.impl.JdkSpiServiceImpl1
com.example.demo.jdkspi.service.impl.JdkSpiServiceImpl2

编写测试类JdkSpiDemo

public class JdkSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ServiceLoader<JdkSpiService> load = ServiceLoader.load(JdkSpiService.class);

        for (JdkSpiService jdkSpiService : load) {
    
    
            jdkSpiService.hello();
        }
    }
}

运行结果如下:
在这里插入图片描述
读取了并加载了配置文件里配置的两个实现。
jdk spi的缺点:
不能按需加载,一旦加载就加载并实例化全部的实现,要通过遍历来寻找需要的实现
并发下获取到的实例是不安全的。

2 dubbo spi

基于jdk spi本身的特点,已经dubbo自身的需要,dubbo自己实现了一套spi。

2.1 基本应用

2.1.1 普通应用

创建一个maven工程,添加如下依赖
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>dubbo-a-my-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dubbo-a-my-demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-common</artifactId>
            <version>2.7.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-config-spring</artifactId>
            <version>2.7.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

创建一个接口SpiService如下,接口上添加@SPI注解

@SPI
public interface SpiService {
    
    

    public void hello(String name);
}

创建一个实现DefaultSpiServiceImpl ,如下

public class DefaultSpiServiceImpl implements SpiService {
    
    
    @Override
    public void hello(String name) {
    
    
        System.out.println("default hello dubbo spi "+name);
    }
}

在resources目录下创建META-INF/dubbo目录,创建com.example.dubboamydemo.spi.service.SpiService文件,等号左边为实现类的key,右边是实现类的全限定名,如下:

defaultSpiServiceImpl=com.example.dubboamydemo.spi.service.impl.DefaultSpiServiceImpl

创建测试类,通过ExtensionLoader.getExtensionLoader拿到ExtensionLoader,通过调用getExtension方法传入配置文件中的key获取到实现类的实例。

扫描二维码关注公众号,回复: 12464441 查看本文章
public class DubboSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);
        SpiService defaultSpiServiceImpl = extensionLoader.getExtension("defaultSpiServiceImpl");
        defaultSpiServiceImpl.hello("default");

    }
}

运行main方法,结果如下:
在这里插入图片描述
可以看到执行了我们的默认实现。

dubbo的spi还可以配置默认的实现类,比如现在修改SpiService接口,在@SPI注解上添加参数

@SPI("defaultSpiServiceImpl")
public interface SpiService {
    
    

    public void hello(String name);
}

修改测试类

public class DubboSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);
        
        SpiService defaultExtension = extensionLoader.getDefaultExtension();
        
       defaultExtension.hello("default123");

    }
}

运行测类,结果如下
在这里插入图片描述
可以看到,通过在@SPI注解添加名称,将名称对应的实现类作为默认实现,在调用extensionLoader.getDefaultExtension()那到实现类实例。

2.1.2 自适应 @Adaptive

dubbo的SPI可以通过@Adaptive注解实现自适应的扩展点。@Adaptive可以配置在类上,也可配置在方法上,配置在类上是指配置在实现类上,配置在方法上是指配置在接口的方法上。

2.1.2.1 类上的@Adaptive

现在我们再增加一个接口实现AdaptiveSpiServiceImpl ,并再类上配置@Adaptive注解,如下:

@Adaptive
public class AdaptiveSpiServiceImpl implements SpiService {
    
    
    @Override
    public void hello(String name) {
    
    
        System.out.println("adaptive hello dubbo spi "+ name);
    }
}

修改配置文件com.example.dubboamydemo.spi.service.SpiService,将AdaptiveSpiServiceImpl配置进去

defaultSpiServiceImpl=com.example.dubboamydemo.spi.service.impl.DefaultSpiServiceImpl
adaptiveSpiServiceImpl=com.example.dubboamydemo.spi.service.impl.AdaptiveSpiServiceImpl

修改测试类

public class DubboSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);
        
        SpiService adaptiveExtension = extensionLoader.getAdaptiveExtension();
        
        adaptiveExtension.hello("adaptive");
    }
}

运行测试类结果如下:
在这里插入图片描述

2.1.2.2 方法上的@Adaptive

这里需要注意一点,配置在方法上时对方法是有特殊要求的,方法必须要有入参,且第一个参数必须是org.apache.dubbo.common.URL类型。
现在修改SpiService接口,添加一个方法如下,在方法上添加@Adaptive注解,注解里面可以传入一个字符串,dubbo会将字符串作为key,取得URL中的参数,作为实现类的名字,如果不传默认为@SPI注解传入的值作为实现类的名称,如果这两个注解都没有传入值,默认是接口名以大写字母为分割通过点连接,并将大写字母变小写作为key从URL中获取实现类名称,如SpiService这个接口的名称就是:spi.service。

@SPI("defaultSpiServiceImpl")
public interface SpiService {
    
    

    public void hello(String name);
	 @Adaptive("nameKey")
    public void hello(URL url, String name);
}

修改测试类

public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);

        SpiService adaptiveExtension = extensionLoader.getAdaptiveExtension();

        URL url = URL.valueOf("test://12.13.14.11/mm?nameKey=adaptiveSpiServiceImpl");

        adaptiveExtension.hello(url,"adaptive");
    }

注意需要将上面添加在AdaptiveSpiServiceImpl类上的@Adaptive注解注释

//@Adaptive
public class AdaptiveSpiServiceImpl implements SpiService {
    
    
    @Override
    public void hello(String name) {
    
    
        System.out.println("adaptive hello dubbo spi "+ name);
    }

    @Override
    public void hello(URL url, String naame) {
    
    
        System.out.println("adaptiveSpiServiceImpl++"+name);
    }
}

DefaultSpiServiceImpl 类也实现hello(URL url, String name) 方法:

public class DefaultSpiServiceImpl implements SpiService {
    
    
    @Override
    public void hello(String name) {
    
    
        System.out.println("default hello dubbo spi "+name);
    }

    @Override
    public void hello(URL url, String name) {
    
    
        System.out.println("defaultSpiServiceImpl+++"+name);
    }
}

现在运行测试类结果如下:
在这里插入图片描述
可以看到现在获取到的是AdaptiveSpiServiceImpl的实例,

现在修改测试类中的URL,将nameKey的值改为defaultSpiServiceImpl

public class DubboSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);

        SpiService adaptiveExtension = extensionLoader.getAdaptiveExtension();

        URL url = URL.valueOf("test://12.13.14.11/mm?nameKey=defaultSpiServiceImpl");

        adaptiveExtension.hello(url,"adaptive");
    }
}

运行结果如下:
在这里插入图片描述
获取到的是DefaultSpiServiceImpl实例。

2.1.3 通过URL可激活的扩展点

指通过传入的URL的参数判断哪些实现是激活的,即需要加载实例化哪些实现。 通过ExtensionLoader#getActivateExtension方法获取激活的实例集合

2.1.3.1 指定激活的实现类名称

修改测试类

public class DubboSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);

        URL url = URL.valueOf("test://12.13.14.11/mm?nameKey=defaultSpiServiceImpl,adaptiveSpiServiceImpl");
        
        List<SpiService> nameKey = extensionLoader.getActivateExtension(url, "nameKey");

        nameKey.forEach(spiService -> {
    
    
            spiService.hello("daf");
        });
    }
}

URL中参数nameKey指定哪些扩展点需要被激活,getActivateExtension方法传入url和url中指定激活扩展点名称的key。
运行main方法
在这里插入图片描述
可以看到defaultSpiServiceImpl,adaptiveSpiServiceImpl被激活。

2.1.3.2 @Activate 指定激活的扩展点

新增一个实现类ActivateSpiServiceImpl ,添加@Activate注解,该注解有两个重要属性value和group,这两个属性其实代表了两个条件,只有两个属性代表的条件同时为true,@Activate注解修饰的实现类就会被激活。
value:是一个字符串数组,格式为{“key1:value1”,“key2:value2”},key、value表示URL中参数的一个键值对,即传入的URL中要包含value数组中的一个键值对,那么该条件就为true,如果不配该属性,那么这个条件默认为true。这里的value值并不表示实现类在配置文件中的key,可以是任意值,只要URL中也有此键值对,那么该条件就为true
group:是一个字符串数组,只要数组中包含,调用ExtensionLoader#getActivateExtension方法传入的group值,那么该条件就为true,如果不配该属性,那么这个条件默认为true。

@Activate(value = {
    
    "key1:value1"})
public class ActivateSpiServiceImpl implements SpiService {
    
    
    @Override
    public void hello(String name) {
    
    
        System.out.println("activate hello dubbo spi "+ name);
    }

    @Activate
    @Override
    public void hello(URL url, String name) {
    
    
        System.out.println("ActivateSpiServiceImpl +++ "+name);
    }
}

修改测试类,url中增加键值对key1=value1

public class DubboSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);

        URL url = URL.valueOf("test://12.13.14.11/mm?nameKey=defaultSpiServiceImpl,adaptiveSpiServiceImpl&key1=value1");
        List<SpiService> nameKey = extensionLoader.getActivateExtension(url, "nameKey");

        nameKey.forEach(spiService -> {
    
    
            spiService.hello("daf");
        });
    }
}

运行main方法
在这里插入图片描述
可以看到加上nameKey指定的激活类,一共加载了3个实现类。

ExtensionLoader.getActivateExtension方法还可以传入group,过滤激活类。

修改ActivateSpiServiceImpl ,@Activate注解中加入group属性,值为group2。

@Activate(value = {
    
    "key1:value1"},group = "group2")
public class ActivateSpiServiceImpl implements SpiService {
    
    
    @Override
    public void hello(String name) {
    
    
        System.out.println("activate hello dubbo spi "+ name);
    }

    @Activate
    @Override
    public void hello(URL url, String name) {
    
    
        System.out.println("ActivateSpiServiceImpl +++ "+name);
    }
}

修改测试类,ExtensionLoader#getActivateExtension方法中传入group参数,为group1

public class DubboSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);

        URL url = URL.valueOf("test://12.13.14.11/mm?nameKey=defaultSpiServiceImpl,adaptiveSpiServiceImpl&key1=value1");
        List<SpiService> nameKey = extensionLoader.getActivateExtension(url, "nameKey", "group1");

        nameKey.forEach(spiService -> {
    
    
            spiService.hello("daf");
        });
    }
}

运行main方法
在这里插入图片描述
由于@Activate注解传入的group不包含ExtensionLoader#getActivateExtension方法传入的group值,所以@Activate修饰的类没有被激活。

以上就是dubbo的spi几种常见的简单用法。下面开始分析其源码实现。

2.2 dubbo SPI 源码详解

2.2.1 ExtensionLoader

由上面的几个例子可以看到使用dubbo的SPI,首先就需要获取的ExtensionLoader实例,获取实例是通过调用其静态方法ExtensionLoader.getExtensionLoader完成的,现在进入getExtensionLoader方法。

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 an interface!");
        }
        if (!withExtensionAnnotation(type)) {
    
    
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }

        /**
         *
         * EXTENSION_LOADERS 缓存ExtensionLoader,一个类型一个 ExtensionLoader
         * 从缓存EXTENSION_LOADERS 获取该类型的ExtensionLoader,不存在则创建一个
         * new EXTENSION_LOADERS 里主要是创建了ExtensionFactory,这是用来做依赖注入的,优先从dubbo spi里面取,
         * 取不到再从spring里面取。
         */
        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;
    }

由上面代码可以看到getExtensionLoader方法并没有做太多的事情,先做了对传入的class进行交易,然后看EXTENSION_LOADERS缓存中是否存在传入类型的一个ExtensionLoader,不存在就new一个,并且放入缓存;ExtensionLoader的构造方法中给两个遍历赋了值,一个是type,一个是objectFactory,其中objectFactory与dubbo的依赖注入有关,这个放到后面说。

2.2.2 ExtensionLoader#getExtensionClasses

首先我们研究getExtensionClasses这个方法,这个方法的作用就是读取配置文件获取实现类型,根据不同的条件缓存将Class<?>放在不同的缓存中。

private Map<String, Class<?>> getExtensionClasses() {
    
    
        /**
         * cachedClasses缓存了除了@Adaptive修饰的类和Wrapper类型的类剩下的接口配置文件中的所有类
         * 以key-value形式放到map中。
         */
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
    
    
            synchronized (cachedClasses) {
    
    
                classes = cachedClasses.get();
                if (classes == null) {
    
    
                    classes = loadExtensionClasses();
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

先从缓存里取,缓存里没有再调用loadExtensionClasses方法加载,进入loadExtensionClasses方法

private Map<String, Class<?>> loadExtensionClasses() {
    
    
        cacheDefaultExtensionName();

        Map<String, Class<?>> extensionClasses = new HashMap<>();

        /**
         * strategies通过jdk的SPI配置的加载配置的策略。
         */
        for (LoadingStrategy strategy : strategies) {
    
    
            loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
            loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
        }

        return extensionClasses;
    }

这里使用策略模式,加载加载目录,strategies是ExtensionLoader实例化时调用loadLoadingStrategies()方法赋值的,而loadLoadingStrategies方法即通过java的spi获取到LoadingStrategy 接口配置的三个实现

org.apache.dubbo.common.extension.DubboInternalLoadingStrategy
org.apache.dubbo.common.extension.DubboLoadingStrategy
org.apache.dubbo.common.extension.ServicesLoadingStrategy

三个实现类中分别配置了一个加载路径:
META-INF/dubbo/internal/:通常时dubbo内部使用的
META-INF/dubbo/
META-INF/services/:可以和jdk的spi兼容
现在回到loadExtensionClasses,可以看到循环里调用两次loadDirectory方法,这是为了兼容以前阿里巴巴的类。进入loadDirectory方法

private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
                               boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
    
    
        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, overridden, excludedPackages);
                }
            }
        } catch (Throwable t) {
    
    
            logger.error("Exception occurred when loading extension class (interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

该方法根据策略中配置的路径加载配置文件,然后调用loadResource方法解析配置文件,进入loadResource方法,该方法逐行读取,解析出配置的实现类的key和全类限定名,根据配置的全类限定名加载类。然后调用loadClass,进入loadClass。

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
                           boolean overridden) 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.");
        }
        /**
         * 如果实现类上有Adaptive注解,就赋值给cachedAdaptiveClass缓存,方法结束
         */
        if (clazz.isAnnotationPresent(Adaptive.class)) {
    
    
            cacheAdaptiveClass(clazz, overridden);
            /**
             * 如果实现类时Wrapper类,就缓存到cachedWrapperClasses中,方法结束
             * Wrapper类就是该实现类有一个传入本身类型参数的构造函数。
             */
        } else if (isWrapperClass(clazz)) {
    
    
            cacheWrapperClass(clazz);
        } else {
    
    
            clazz.getConstructor();
            if (StringUtils.isEmpty(name)) {
    
    
                /**
                 * 如果配置文件中没有配置key,看实现类中是否有被Extension注解修饰,并配置了key
                 * 如果没有则生成一个key,生成规则:
                 * 假如实现类名称是以接口名结尾,则截取接口名前面的字段,并转为小写作为key
                 * 如果实现类名称不是以接口名结尾,直接转为小写作为key
                 */
                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)) {
    
    
                /**
                 * 如果该类被Activate注解修饰,则将Activate注解缓存到cachedActivates
                 */
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
    
    
                    cacheName(clazz, n);
                    /**
                     * 将配置文件中的名称和class,以键值对的形式存入extensionClasses
                     */
                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                }
            }
        }
    }

根据实现类的不同配置,将加载的类型存入不同的缓存,若配置文件中没有配置key,会生成一个key,生成规则看代码注释。

以上就是ExtensionLoader#getExtensionClasses的全部逻辑。

2.2.3 ExtensionLoader#getExtension获取普通的扩展对象

getExtension(String name),重载了一个两个参数的方法如下:

public T getExtension(String name, boolean wrap) {
    
    
        if (StringUtils.isEmpty(name)) {
    
    
            throw new IllegalArgumentException("Extension name == null");
        }
        if ("true".equals(name)) {
    
    
            return getDefaultExtension();
        }
        /**
         * 这里为包装成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, wrap);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

该方法就是首先从缓存中获取实例,如果缓存中没有对象则调用createExtension方法;加载配置文件配置的实现类,并依次将其实例化、依赖注入、aop逻辑、初始化,后返回实例。

private T createExtension(String name, boolean wrap) {
    
    
        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);
            }

            /**
             * 依赖注入,判断是否有setter方法,即以set开头,只有一个参数的方法,
             * 根据传入的参数作为类型。
             * 截取方法名除set后的部分,并将首字母小写,作为依赖属性的名称。
             * 从dudbbo的spi里或srping容器里取得依赖对象。
             */
            injectExtension(instance);


            /**
             * 通过Wrapper实现aop逻辑
             */
            if (wrap) {
    
    

                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
    
    
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }

                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
    
    
                    for (Class<?> wrapperClass : wrapperClassesList) {
    
    
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
    
    
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }

            /**
             * 初始化逻辑,实现了Lifecycle接口 会调用其initialize方法,完成一些初始逻辑
             */
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
    
    
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

2.2.3.1 injectExtension 依赖注入

dubbo的依赖注入逻辑:
1.获取类型的所有方法,断是否有setter方法,即以set开头,只有一个参数的方法,有则继续。
2.如果set方法被DisableInject注解修饰则不注入
3.获取依赖注入扩展类的名字,截取方法名除set后的部分,并将首字母小写,作为依赖属性的名称。
4.使用set方法传入参数的类型和根据set方法名获取到的名称作为参数获取一个扩展点实例。
5.如果获取到的依赖实例不为空,执行setter方法。

private T injectExtension(T instance) {
    
    

        if (objectFactory == null) {
    
    
            return instance;
        }

        try {
    
    
            /**
             *  获取类型的所有方法,断是否有setter方法,即以set开头,只有一个参数的方法。
             */
            for (Method method : instance.getClass().getMethods()) {
    
    
                if (!isSetter(method)) {
    
    
                    continue;
                }
                /**
                 * Check {@link DisableInject} to see if we need auto injection for this property
                 */
                /**
                 * 如果set方法被DisableInject注解修饰则不注入
                 */
                if (method.getAnnotation(DisableInject.class) != null) {
    
    
                    continue;
                }
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
    
    
                    continue;
                }

                try {
    
    
                    /**
                     * 获取依赖注入扩展类的名字,截取方法名除set后的部分,并将首字母小写,作为依赖属性的名称
                     */
                    String property = getSetterProperty(method);
                    /**
                     * 使用set方法传入参数的类型和根据set方法名获取到的名称作为参数获取一个扩展点实例。
                     * 如果是注入dubbo spi的依赖那么名称没有用。是根据接口的@Adaptive注解传入的key或根据接口名默认生成的key
                     * 从URL中获取的参数值作为的扩展点名称。
                     */
                    Object object = objectFactory.getExtension(pt, property);
                    /**
                     * 如果获取到的依赖实例不为空,执行setter方法
                     */
                    if (object != null) {
    
    
                        method.invoke(instance, object);
                    }
                } catch (Exception e) {
    
    
                    logger.error("Failed to inject via method " + method.getName()
                            + " of interface " + type.getName() + ": " + e.getMessage(), e);
                }

            }
        } catch (Exception e) {
    
    
            logger.error(e.getMessage(), e);
        }
        return instance;
    }

由上面代码可以看出依赖对象是通过objectFactory.getExtension(pt, property);从一个对象工厂中获取的objectFactory的类型是ExtensionFactory,objectFactory是在ExtensionLoader构造方法中初始化的。

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

可以看到ObjectFactory通过dubbo SPI 获取的自适应扩展类。查找ExtensionFactory 所有的实现,发现由一个AdaptiveExtensionFactory的实现,发下其被@Adaptive注解,所有获取到的就是这个类。

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
    
    

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
    
    
        /**
         * 通过dubbo SPI获取到所有普通的扩展点实例。
         */
        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);
    }

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

}

该类在构造方法中通过dubbo SPI获取到所有普通的扩展点实例,这里的配置文件org.apache.dubbo.common.extension.ExtensionFactory有两个,一个在dubbo-common的jar里,另一个在dubbo-config-spring的jar包里。
dubbo-common:

adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory

dubbo-config-spring

spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory

所以最后AdaptiveExtensionFactory类的factories中有两个扩展点实例SpiExtensionFactory、SpringExtensionFactory,并且SpiExtensionFactory的顺序排在前面。

下面我们来看看ExtensionFactory接口的抽象方法getExtension,该方法就是循环factories,调用其getExtension方法,如果能获取到实例对象就直接返回,如果都没有就返回null;
先看SpiExtensionFactory的方法

public class SpiExtensionFactory implements ExtensionFactory {
    
    

    @Override
    public <T> T getExtension(Class<T> type, String name) {
    
    
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
    
    
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            /**
             * 依赖注入的类行必须存在一个普通的扩展点类型
             * 返回的对象是自适应扩展点对象
             * 由上面两个条件可以看出如果要使用dubbo的SPI依赖注入:
             *  该接口必须存在自适应类扩展类和普通扩展类。
             * 所以需要在接口的方法上配置@Adaptive注解,并且接口必须有一个第一个参数是URL的方法。
             */
            if (!loader.getSupportedExtensions().isEmpty()) {
    
    
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}

通过dubbo 的SPI 做依赖注入,需要在接口的方法上配置@Adaptive注解,并且接口必须有一个第一个参数是URL的方法,还需要由普通的扩展类。
如果SpiExtensionFactory获取到的依赖实例我null,再从SpringExtensionFactory获取,该类就是从spring容器中获取对象。

2.2.3.1.1 dubbo SPI依赖注入用法示例

SpiService如下:

@SPI("defaultSpiServiceImpl")
public interface SpiService {
    
    

    public void hello(String name);

    @Adaptive("nameKey")
    public void hello(URL url, String name);
}

com.example.dubboamydemo.spi.service.SpiService配置文件中配置了2.1章节所创建的示例类

defaultSpiServiceImpl=com.example.dubboamydemo.spi.service.impl.DefaultSpiServiceImpl
adaptiveSpiServiceImpl=com.example.dubboamydemo.spi.service.impl.AdaptiveSpiServiceImpl
activateSpiServiceImpl=com.example.dubboamydemo.spi.service.impl.ActivateSpiServiceImpl

DefaultSpiServiceImpl 的实现如下:调用一个参数的hello方法,通过URL中传入的nameKey的值获取到ActivateSpiServiceImpl

public class DefaultSpiServiceImpl implements SpiService {
    
    
    SpiService spiService;
    @Override
    public void hello(String name) {
    
    
        URL url = URL.valueOf("test://12.13.14.11/mm?nameKey=activateSpiServiceImpl");
        spiService.hello(url,name);
    }

    @Override
    public void hello(URL url, String name) {
    
    
        System.out.println("defaultSpiServiceImpl+++"+name);
    }

    public void setActivateSpiServiceImpl(SpiService spiService) {
    
    
        this.spiService = spiService;
    }
}

测试类如下:

public class DubboSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);

        SpiService defaultSpiServiceImpl = extensionLoader.getExtension("defaultSpiServiceImpl");

        defaultSpiServiceImpl.hello("default");


    }
}

运行测试类:
在这里插入图片描述
可见最后调用的是ActivateSpiServiceImpl,依赖注入成功。

2.2.3.2 基于Wrapper AOP实现

dubbo的aop时通过Wrapper包装类实现的,Wrapper包装类是指实现了接口类型,并且提供了一个传入接口类型参数的构造方法的类。
下面是ExtensionLoader#createExtension方法中AOP的逻辑:
1.在cachedWrapperClasses缓存中获取所有的Wrapper类,cachedWrapperClasses缓存是getExtensionClasses方法加载类时,如果时Wrapper类就放入缓存。
2.判断Wrapper类上是否有Wrapper注解,没有Wrapper注解,则对所有的扩展类都用此Wrapper类包装,即所有的扩展类都会用到这个aop,有Wrapper注解,Wrapper注解有两个属性matches和mismatches,都是数组类型,如果获取的扩展类型的名称包含在matches中并且不包含在mismatches中,那么该名称对应的扩展类就会被这个Wrapper包装。

/**
             * 通过Wrapper实现aop逻辑
             */
            if (wrap) {
    
    

                /**
                 * 在cachedWrapperClasses缓存中获取所有的Wrapper类。
                 * Wrapper包装类是指实现了接口类型,并且提供了一个传入接口类型参数的构造方法的类。
                 * cachedWrapperClasses缓存是getExtensionClasses方法加载类时,如果时Wrapper类就放入缓存
                 */
                List<Class<?>> wrapperClassesList = new ArrayList<>();
                if (cachedWrapperClasses != null) {
    
    
                    wrapperClassesList.addAll(cachedWrapperClasses);
                    wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                    Collections.reverse(wrapperClassesList);
                }

                /**
                 * 判断Wrapper类上是否有Wrapper注解
                 *      1.没有Wrapper注解,则对所有的扩展类都用此Wrapper类包装,即所有的扩展类都会用到这个aop
                 *      2.有Wrapper注解,如果Wrapper注解有两个属性matches和mismatches,都时数组类型
                 *           如果获取的扩展类型的名称包含在matches中并且不包含在mismatches中,那么该名称对应的扩展类就会被这个Wrapper包装。
                 */
                if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
    
    
                    for (Class<?> wrapperClass : wrapperClassesList) {
    
    
                        Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                        if (wrapper == null
                                || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
    
    
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }
2.2.3.2.1 dubbo AOP 用法示例

添加一个Wrapper类

public class SpiServiceWrapper implements SpiService {
    
    
    SpiService spiService;
    public SpiServiceWrapper(SpiService spiService) {
    
    
        this.spiService = spiService;
    }
    @Override
    public void hello(String name) {
    
    
        System.out.println("aop 调用前执行点逻辑");
        spiService.hello("aop");
        System.out.println("aop 调用后执行点逻辑");
    }

    @Override
    public void hello(URL url, String name) {
    
    
        System.out.println("url aop 调用前执行点逻辑");
        spiService.hello(url,name);
        System.out.println("url aop 调用后执行点逻辑");
    }
}

在com.example.dubboamydemo.spi.service.SpiService配置文件中加入Wrapper类的配置

defaultSpiServiceImpl=com.example.dubboamydemo.spi.service.impl.DefaultSpiServiceImpl
adaptiveSpiServiceImpl=com.example.dubboamydemo.spi.service.impl.AdaptiveSpiServiceImpl
activateSpiServiceImpl=com.example.dubboamydemo.spi.service.impl.ActivateSpiServiceImpl
wrapper=com.example.dubboamydemo.spi.service.impl.SpiServiceWrapper

测试类如下

public class DubboSpiDemo {
    
    
    public static void main(String[] args) {
    
    
        ExtensionLoader<SpiService> extensionLoader = ExtensionLoader.getExtensionLoader(SpiService.class);

        SpiService defaultSpiServiceImpl = extensionLoader.getExtension("defaultSpiServiceImpl");
        defaultSpiServiceImpl.hello("default");


    }
}

运行main方法结果如下:

在这里插入图片描述
由于使用了上面依赖注入的代码所以结果入上,不过可以看出我们aop的逻辑已经执行了。

2.2.3.3 initExtension 初始化

如果扩展类实现了Lifecycle接口,那么会执行其initialize方法,执行初始化逻辑。

private void initExtension(T instance) {
    
    
        if (instance instanceof Lifecycle) {
    
    
            Lifecycle lifecycle = (Lifecycle) instance;
            lifecycle.initialize();
        }
    }

2.2.4 ExtensionLoader#getAdaptiveExtension获取自适应对象

进入getAdaptiveExtension方法:

public T getAdaptiveExtension() {
    
    
        /**
         * 从cachedAdaptiveInstance缓存中获取自适应扩展类
         * cachedAdaptiveInstance是getExtensionClasses方法,加载配置文件时
         * 如果配置的类上面有@Adaptive注解就会放入这个缓存。
         */
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
    
    
            if (createAdaptiveInstanceError != null) {
    
    
                throw new IllegalStateException("Failed to create adaptive instance: " +
                        createAdaptiveInstanceError.toString(),
                        createAdaptiveInstanceError);
            }

            synchronized (cachedAdaptiveInstance) {
    
    
                instance = cachedAdaptiveInstance.get();
                /**
                 * 如果缓存中存在了自适应类,就直接返回
                 */
                if (instance == null) {
    
    
                    try {
    
    
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
    
    
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        }

        return (T) instance;
    }

该方法首先从cachedAdaptiveInstance缓存中获取自适应实例,如果存在就直接返回,不存在调用createAdaptiveExtension方法创建自适应实例。

private T createAdaptiveExtension() {
    
    
        try {
    
    
            /**
             * 1.getAdaptiveExtensionClass方法获取自适应扩展类型。从缓存获取,缓存中没有
             * 就自己生成一个类型。
             * 2.获取到自适应扩展类型后newInstance实例化。
             * 3.调用injectExtension方法,完成依赖注入。 
             */
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
    
    
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

该方法就是获取到自适应扩展类型,反射实例化,然后依赖注入,最后返回实例。
下面进入getAdaptiveExtensionClass看是如何获取到自适应扩展类型:

private Class<?> getAdaptiveExtensionClass() {
    
    
        getExtensionClasses();
        /**
         * 首先从cachedAdaptiveClass缓存中获取自适应扩展类型,
         * cachedAdaptiveClass缓存是getExtensionClasses方法加载配置文件中的类时,如果类上面
         * 有被@Adaptive修饰,就会放到这个缓存。
         */
        if (cachedAdaptiveClass != null) {
    
    
            return cachedAdaptiveClass;
        }
        /**
         * 如果cachedAdaptiveClass缓存的扩展类类型为空,那么会生成一个自适应扩展类型。
         */
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

getAdaptiveExtensionClass方法首先从cachedAdaptiveClass缓存中获取自适应扩展类型,cachedAdaptiveClass缓存是getExtensionClasses方法加载配置文件中的类时,如果类上面有被@Adaptive修饰,就会放到这个缓存。
如果缓存里有值则直接返回。
如果为null,调用createAdaptiveExtensionClass方法通过javassist字节码技术生成一个自适应扩展类,这里就是@Adaptive修饰在接口方法上的逻辑。
生成的自适应类如下,具体的生成逻辑这里不做概述。

package com.example.dubboamydemo.spi.service;

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

public class SpiService$Adaptive implements com.example.dubboamydemo.spi.service.SpiService {
    
    
	
	public void hello(java.lang.String arg0)  {
    
    
		throw new UnsupportedOperationException("The method public abstract void com.example.dubboamydemo.spi.service.SpiService.hello(java.lang.String) of interface com.example.dubboamydemo.spi.service.SpiService is not adaptive method!");
	}

	public void hello(org.apache.dubbo.common.URL arg0, java.lang.String arg1)  {
    
    
		if (arg0 == null) throw new IllegalArgumentException("url == null");
		org.apache.dubbo.common.URL url = arg0;
		String extName = url.getParameter("nameKey", "defaultSpiServiceImpl");
		if(extName == null) throw new IllegalStateException("Failed to get extension (com.example.dubboamydemo.spi.service.SpiService) name from url (" + url.toString() + ") use keys([nameKey])");
		com.example.dubboamydemo.spi.service.SpiService extension = (com.example.dubboamydemo.spi.service.SpiService)ExtensionLoader.getExtensionLoader(com.example.dubboamydemo.spi.service.SpiService.class).getExtension(extName);
		extension.hello(arg0, arg1);
	}
}

其中第一个参数不是URL参数的方法都被抛出异常。有第一个参数是URL的方法,首先会根据url获取需要的扩展点类名称url.getParameter(“nameKey”, “defaultSpiServiceImpl”),其中nameKey的获取逻辑如下:
1.首先取Adaptive注解上面是否配置了名称,如果配置了直接返回
2.如果Adaptive注解上面没有配置名称,会根据接口名生成一个,生成规则:通过大写字母将接口名分割,以点号连接,再将所有的大写字母转为小写

private String[] getMethodAdaptiveValue(Adaptive adaptiveAnnotation) {
    
    
        /**
         * 首先取Adaptive注解上面是否配置了名称,如果配置了直接返回
         */
        String[] value = adaptiveAnnotation.value();
        // value is not set, use the value generated from class name as the key
        if (value.length == 0) {
    
    
            /**
             * 如果Adaptive注解上面没有配置名称,会根据接口名生成一个,生成规则:
             * 通过大写字母将接口名分割,以点号连接,再将所有的大写字母转为小写
             */
            String splitName = StringUtils.camelToSplitName(type.getSimpleName(), ".");
            value = new String[]{
    
    splitName};
        }
        return value;
    }

2.2.4 ExtensionLoader#getActivateExtension 获取所有激活的扩展点

getActivateExtension 首先通过传入的key从URL中获取配置的激活的扩展点名称,可配置多个以逗号隔开。

public List<T> getActivateExtension(URL url, String key, String group) {
    
    
        String value = url.getParameter(key);
        return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
    }

进入重载方法,改方法完成了对激活类的加载:
1.判断url中配置的扩展点名字是否包含-default,不包含则进入第2点,包含则跳过第2点
2.循环cachedActivates缓存中所有被@Activate注解修饰的扩展点,判断group配置的值中是否包含方法入参传入的group,如果包含则为true,判断value中配置的key:value是包含一个URL中的参数,如果有则为true,如果上面两个条件都为true,那么被@Activate修饰的扩展点将会被激活。
3.通过方法入参key从URL获取到的名称所对应的扩展点类会被激活。

public List<T> getActivateExtension(URL url, String[] values, String group) {
    
    
        List<T> activateExtensions = new ArrayList<>();
        List<String> names = values == null ? new ArrayList<>(0) : asList(values);
        /**
         * 判断url中配置的扩展点名字是否包含-default
         */
        if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
    
    
            /**
             * 加载扩展配置文件,加载所有配置的扩展类
             */
            getExtensionClasses();
            /**
             * 循环cachedActivates缓存中所有被@Activate注解修饰的扩展点
             * 获取@Activate中配置的group和value
             * 判断group配置的值中是否包含方法入参传入的group,如果包含则为true
             * 判断value中配置的key:value是包含一个URL中的参数,如果有则为true
             * 如果上面两个条件都为true,那么被@Activate修饰的扩展点将会被激活。
             */
            for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
    
    
                String name = entry.getKey();
                Object activate = entry.getValue();

                String[] activateGroup, activateValue;

                if (activate instanceof Activate) {
    
    
                    activateGroup = ((Activate) activate).group();
                    activateValue = ((Activate) activate).value();
                } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
    
    
                    activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
                    activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
                } else {
    
    
                    continue;
                }
                /**
                 * 判断group配置的值中是否包含方法入参传入的group,如果包含则为true
                 */
                if (isMatchGroup(group, activateGroup)
                        && !names.contains(name)
                        && !names.contains(REMOVE_VALUE_PREFIX + name)
                        /**
                         * 判断value中配置的key:value是包含一个URL中的参数,如果有则为true
                         */
                        && isActive(activateValue, url)) {
    
    
                    activateExtensions.add(getExtension(name));
                }
            }
            activateExtensions.sort(ActivateComparator.COMPARATOR);
        }
        List<T> loadedExtensions = new ArrayList<>();
        /**
         * 通过方法入参key从URL获取到的名称所对应的扩展点类会被激活
         */
        for (int i = 0; i < names.size(); i++) {
    
    
            String name = names.get(i);
            if (!name.startsWith(REMOVE_VALUE_PREFIX)
                    && !names.contains(REMOVE_VALUE_PREFIX + name)) {
    
    
                if (DEFAULT_KEY.equals(name)) {
    
    
                    if (!loadedExtensions.isEmpty()) {
    
    
                        activateExtensions.addAll(0, loadedExtensions);
                        loadedExtensions.clear();
                    }
                } else {
    
    
                    loadedExtensions.add(getExtension(name));
                }
            }
        }
        if (!loadedExtensions.isEmpty()) {
    
    
            activateExtensions.addAll(loadedExtensions);
        }
        return activateExtensions;
    }

猜你喜欢

转载自blog.csdn.net/qq_36706941/article/details/113731040