我是靠谱客的博主 内向鱼,最近开发中收集的这篇文章主要介绍dubbo SPI 的实现前言一、关于dubbo spi二、小例子三、原理分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 前言
  • 一、关于dubbo spi
    • 1、为什么dubbo没有采用java的SPI?
    • 2、dubbo spi的改进
    • 3、dubbo spi的约定
  • 二、小例子
    • 1.创建接口
    • 2.创建实现类
    • 3、创建配置文件
    • 4、使用dubbo spi加载实现类
  • 三、原理分析
    • 1、我们以`ServiceConfig`类的`protocol ` 对象为例
    • 2、`getExtensionLoader(Class type)`
    • 3、`getAdaptiveExtension()`
    • 4、`getExtension(String name)`


前言

我们之前在《JAVA SPI 机制》中了解了java自带的SPI功能,而dubbo也实现了一套自己的SPI机制。

一、关于dubbo spi

1、为什么dubbo没有采用java的SPI?

因为java的SPI加载实现类时,只能通过迭代器遍历的方式获取加载的实现类。
就算ServiceLoader有延迟加载的功能,那如果先加载的是我们不需要的类,ServiceLoader仍会对其实例化,这就造成了一种资源的浪费;
如果加载类时,该类的初始化时间很长,那我们的程序还要等待该类加载完后再执行,就会影响我们程序的性能。
由于ServiceLoader的内部类LazyIterator并没有提供锁机制来保证线程安全,所以多线程使用ServiceLoader对象时,会存在线程安全的问题。

2、dubbo spi的改进

  • dubbo spi提供了对扩展点的IoC和AOP的支持,一个扩展点可以直接setter进其他的扩展点
  • dubbo spi可以根据接口的@SPI的内容加载对应的是实现类

3、dubbo spi的约定

  • SPI文件可以存放在classpath下的META-INF/dubbo/internal/、META-INF/dubbo/、META-INF/services/3个目录的任意一个目录,并且文件名为接口的全限定性类名。
  • SPI文件内容格式为,扩展名=具体的类名,如cat=com.wgc.dubbo.spi.impl.Cat
  • 在使用时,需要在接口上增加@SPI注解

二、小例子

1.创建接口

代码如下(示例):

package com.wgc.dubbo.spi;

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

@SPI
public interface Animal {

    /**
     * 输出动物叫声
     */
    void say();
}

2.创建实现类

代码如下(示例):

package com.wgc.dubbo.spi.impl;

import com.wgc.dubbo.spi.Animal;

public class Dog implements Animal {
    @Override
    public void say() {
        System.out.println("汪汪");
    }
}

package com.wgc.dubbo.spi.impl;

import com.wgc.dubbo.spi.Animal;

public class Cat implements Animal {

    @Override
    public void say() {
        System.out.println("喵喵");
    }
}

3、创建配置文件

文件创建路径如图所示
文件内容为:

cat=com.wgc.dubbo.spi.impl.Cat
dog=com.wgc.dubbo.spi.impl.Dog

4、使用dubbo spi加载实现类

package com.wgc.dubbo.spi;

import java.sql.SQLException;
import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class SpiMainClass {
    public static void main(String[] args) throws SQLException {
    	Animal cat = ExtensionLoader.getExtensionLoader(Animal.class).getExtension("cat");
    	cat.say();
    	System.out.println("---------------------");
    	Animal dog = ExtensionLoader.getExtensionLoader(Animal.class).getExtension("dog");
    	dog.say();
    }
}

上面代码运行结果为:

喵喵
---------------------
汪汪

三、原理分析

dubbo spi中获取对象都是通过ExtensionLoader<T>类的 getExtension(String name)方法实现的。
其实现路径为:

  1. getExtensionLoader(Class<T> type) 该接口new 了一个ExtensionLoader,并将其缓存
  2. getAdaptiveExtension() 获取一个扩展装饰类的对象,我们通过这个扩展装饰类来根据@SPI的值选取具体的实现类
  3. getExtension(String name) 根据传入的参数name,获取SPI配置中对应的实现类对象

下面介绍下这3个方法(只留下了源码中重要的部分)

1、我们以ServiceConfig类的protocol 对象为例

源码如下:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;

/**
 * Protocol. (API/SPI, Singleton, ThreadSafe)
 */
@SPI("dubbo")
public interface Protocol {

    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    void destroy();

}

我们在来看下dubbo工程中classpath下所有protocol对应的SPI文件的内容:

injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol

2、getExtensionLoader(Class<T> type)

  • 验证type上是否有@SPI注解
  • 从缓存EXTENSION_LOADERS中获取type对应的ExtensionLoader
  • 如果缓存中不存在,就new ExtensionLoader(type)并将其放入缓存

源码如下:

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    
    // ...此处省略了一些简单的验证代码...
   
    // 如果接口上没有@SPI注解就抛出异常
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    }
    // EXTENSION_LOADERS是一个ConcurrentMap<Class<?>, ExtensionLoader<?>>类型的成员变量,用来缓存ExtensionLoader对象
    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对象,所以会执行new ExtensionLoader(type)方法。
ExtensionLoader(type)代码如下:

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

可以看到objectFactory = ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
ExtensionLoader.getExtensionLoader(ExtensionFactory.class)也会执行new ExtensionLoader<T>(type)这行代码来获取type=ExtensionFactory.classExtensionLoader对象,此时ExtensionFactoryExtensionLoader对象的属性为:

type=ExtensionFactory.class;
objectFactory=null;

这几步功能如下图所示:
在这里插入图片描述

获取到ExtensionFactoryExtensionLoader对象后,会执行getAdaptiveExtension()方法,该方法最终返回的结果就是我们调用getExtensionLoader(Protocol.class)方法返回对象的objectFactory的值,为AdaptiveExtensionFactory[SpiExtensionFactory, SpringExtensionFactory]对象。

3、getAdaptiveExtension()

该方法的作用是:
    获取一个扩展装饰类的对象。这个类有一个规则:如果@Adaptive注解在类上,就获取一个装饰类(由类本身实现),如果@Adaptive注解在方法上,就获取一个动态代理类(由代码模板生成)例如:Protocol$Adaptive对象。

  • 1.getAdaptiveExtension()
        1)从缓存对象cachedAdaptiveInstance中获取装饰类
        2)如果缓存中不存在,则调用createAdaptiveExtension()获取装饰类,并将其放入缓存中
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;
    }

第一次执行getAdaptiveExtension()时,cachedAdaptiveInstance中不包含目标类,所以会执行createAdaptiveExtension()方法

  • 2.createAdaptiveExtension()
        1)生成装饰类/动态代理类对象
        2)通过injectExtension方法为装饰类对象注入依赖属性值(IoC的setter注入原理
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);
     }
}
  • 3.createAdaptiveExtension()又执行了两个方法injectExtension()方法和getAdaptiveExtensionClass()方法
  • 3.1我们先看下getAdaptiveExtensionClass()
        1)调用getExtensionClasses()为ExtensionLoader类的成员变量赋值
        2)如果在SPI实现类中有包含@Adaptive注解的类,那么该类就是我们要的装饰类,直接返回就可以了
        3)如果没有包含@Adaptive注解的SPI实现类,就由系统生成一个动态代理类
/**
  * 获取泛型T自适应类对象的class对象:
  */
private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    // cachedAdaptiveClass存的是通过SPI取出的包含@Adaptive注解类,这个类是人工实现的装饰类,如果存在的话,就不需要有dubbo编译出一个动态代理类了,所以直接返回就行了
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 走到这一步,说明没有包含@Adaptive注解的人工实现装饰类,这就需要由系统生成一个动态代理类
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
  • 3.1.1 getExtensionClasses()
        1)从缓存中获取扩展装饰类
        2)如果缓存中不存在,则执行loadExtensionClasses();方法获取
private Map<String, Class<?>> getExtensionClasses() {
	// 从缓存中获取已加载的拓展类
    Map<String, Class<?>> classes = cachedClasses.get();
    // 双重检查
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
            	// 加载拓展类
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}
  • 3.1.2 loadExtensionClasses()
        1)解析type变量的@SPI 注解,如果有值,将其值放入成员变量cachedDefaultName中
        2)调用 loadDirectory 方法加载指定文件夹中的配置文件
 private Map<String, Class<?>> loadExtensionClasses() {
        // 获取 SPI 注解,这里的 type 变量是在调用 getExtensionLoader 方法时传入的
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);	
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((value = value.trim()).length() > 0) {
                // 对 SPI 注解内容进行切分
                String[] names = NAME_SEPARATOR.split(value);
                // 检测 SPI 注解内容是否合法,不合法则抛出异常
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                // 对于protocol类来说,names[0]=“dubbo”
                if (names.length == 1) 
                    // 将@SPI(value)的value值存放在成员变量cachedDefaultName 中
                    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;	
    }

通过本方法调用了3个loadDirectory()方法,大家应该就明白我们之前在dubbo SPI约束中说的 SPI配置文件的路径是META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/这3个路径

private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
  • 3.1.3 loadDirectory(Map<String, Class<?>> extensionClasses, String dir)
        1)生成SPI配置文件路径
        2)获取当前类的类加载器
        3)通过类加载器扫描classpath目录下的spi配置文件
        4)循环遍历扫描到的配置文件,并加载其中的类
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
    // fileName = dubbo spi扫描文件夹路径 + type 全限定性类名 
    String fileName = dir + type.getName();
    try {
        Enumeration<java.net.URL> urls;
        // 获取ExtensionLoader的类加载器
        ClassLoader classLoader = findClassLoader();
        // 根据文件名加载所有的同名文件
        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 when load extension class(interface: " +
                type + ", description file: " + fileName + ").", t);
    }
}
  • 3.1.4loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL)
        1)扫描SPI配置文件的每一行,并将其key,value分隔开
        2)调用loadClass方法将加载的value类放入到合适的缓存变量中
 private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
     try {
         BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
         try {
             String line;
             // 按行读取配置内容
             while ((line = reader.readLine()) != null) {
             	// 定位 # 字符
                 final int ci = line.indexOf('#');
                 if (ci >= 0) line = line.substring(0, ci);
                 // 截取 # 之前的字符串,# 之后的内容为注释,需要忽略
                 line = line.trim();
                 if (line.length() > 0) {
                     try {
                         String name = null;
                         int i = line.indexOf('=');
                         if (i > 0) {
                         	// 以等于号 = 为界,截取键与值
                             name = line.substring(0, i).trim();
                             line = line.substring(i + 1).trim();
                         }
                         if (line.length() > 0) {// AdaptiveCompiler JdkCompiler JavassistCompiler
                         	// 加载类,并通过 loadClass 方法对类进行缓存
                             loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                         }
                     } catch (Throwable t) {
                         IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                         exceptions.put(line, e);
                     }
                 }
             }
         } finally {
             reader.close();
         }
     } catch (Throwable t) {
         logger.error("Exception when load extension class(interface: " +
                 type + ", class file: " + resourceURL + ") in " + resourceURL, t);
     }
 }
  • 3.1.5 loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name)
        1)如果clazz中包含@Adaptive注解,将执行赋值操作cachedAdaptiveClass = clazz;
        2)如果clazz中不包含@Adaptive注解,并且构造函数的参数为目标接口(type)类型时:将clazz保存到全局变量的cachedWrapperClasses中;cachedWrapperClasses存放的是在getExtension(String name)的方法中使用AOP的装饰类
        3)如果不满足上面两个条件,进行判断,如果clazz上有@Activate注解时,将clazz作为key存放到全局变量cachedActivates中;
        4)如果不满足条件1和条件2,则将clazz作为value存入到参数extensionClasses中
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 when load extension class(interface: " +
                type + ", class line: " + clazz.getName() + "), class "
                + clazz.getName() + "is not subtype of interface.");
    }
    // 如果class中有@Adaptive注解
    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());
        }
    } else if (isWrapperClass(clazz)) {	
    // 如果class无@Adaptive注解,并且构造函数的参数为目标接口(type)类型时
        Set<Class<?>> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
            wrappers = cachedWrapperClasses;
        }
        wrappers.add(clazz);
    } else {
        clazz.getConstructor();
        if (name == null || name.length() == 0) {
            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 (names != null && names.length > 0) {
        // 获取该类中存在的@Activate注解,如果注解不存在,则返回null
            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());
                }
            }
        }
    }
}

最终在我们加载完Protocol的所有SPI实现类后,这些类存储情况如下所示:

cachedAdaptiveClass= null
cachedWrapperClasses=[
		class com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper, 
		class com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
]
cachedActivates= {}
cachedNames = {
		class com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol=injvm, 
		class com.alibaba.dubbo.registry.integration.RegistryProtocol=registry, 
		class com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol=dubbo, 
		class com.alibaba.dubbo.rpc.support.MockProtocol=mock
}

loadClass方法的参数对象extensionClasses存放内容如下:

{
	registry=class com.alibaba.dubbo.registry.integration.RegistryProtocol,
	injvm=class com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol, 
	dubbo=class com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol, 
	mock=class com.alibaba.dubbo.rpc.support.MockProtocol
}

执行完这个方法后,就会一路返回,直到3.1的getAdaptiveExtensionClass()方法,因为cachedAdaptiveClass对象值为null,所以会执行createAdaptiveExtensionClass()方法

  • 3.1.2 createAdaptiveExtensionClass()

走到这个方法,说明SPI加载的实现类中都没有在类上增加@Adaptive注解,所以需要自动生成和编译一个adaptive动态类
    1)使用createAdaptiveExtensionClassCode()方法,动态生成java代码
    2)根据SPI加载Compiler的实现类
    3)根据compiler编译出动态生成的java代码的class对象

    private Class<?> createAdaptiveExtensionClass() {
	    // 根据模板生成动态了代码
        String code = createAdaptiveExtensionClassCode();
        ClassLoader classLoader = findClassLoader();
        // compiler有两个实现类:AdaptiveCompiler和AbstractCompiler,通过getAdaptiveExtension方法获取的是AdaptiveCompiler
        Compiler compiler = ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();
        // 编译生成动态类的class对象
        return compiler.compile(code, classLoader);
    }

createAdaptiveExtensionClassCode()生成的Protocol接口的动态类代码如下所示:
    根据规则,接口中如果方法上没有@Adaptive注解,那么动态生成的方法体为抛出一个异常

package com.alibaba.dubbo.rpc;

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

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    // 根据规则,Protocol 接口的destroy()方法上没有@Adaptive注解,所以生成的代码方法体为抛出一个异常
    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!");
    }
    // 根据规则,Protocol 接口的getDefaultPort()方法上没有@Adaptive注解,所以生成的代码方法体为抛出一个异常
    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);
    }
}
  • 3.2 injectExtension(T instance)

injectExtension()方法在Protocol获取装饰类时,没有起作用。我们在下面getExtension(String name)中进行讲解

4、getExtension(String name)

getExtension(name)获取查找具有给定名称的扩展类。如果未找到指定的名称,则抛出IllegalStateException异常。这里会根据条件进行IOC和AOP来获取对象

  • 1 getExtension(String name)

    1)判断参数name是否合法,那么为@SPI注解的内容
    2)从缓存中获取扩展类
    3)如果缓存中没有,则调用createExtension(name)方法生成

public T getExtension(String name) {	// javassist  dubbo javassist injvm
        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;
    }
  • 2 createExtension(String name)
        1) 先调用getExtensionClasses()方法获取name对应的class对象,这个方法我们在之前讲过
        2)根据class对象从缓存中取出实例对象
        3)调用injectExtension方法使用IoC注入对象属性值
        4)循环装饰类cachedWrapperClasses(这个对象我们之前讲过),采用了装饰者模式增强目标对象(AOP)。
private T createExtension(String name) {
        // 根据SPI配置文件的key获得value对应的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);
            }
            // 使用IOC注入对象
            injectExtension(instance);	
            // cachedWrapperClasses放的都是class无adaptive注解,并且构造函数的参数为目标接口(type)类型,用来做AOP
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;	
            if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            	// 通过装饰者模式,使用AOP包装instance对象
                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);
        }
    }
  • 3 injectExtension(T instance)Dubbo IOC 是通过 setter 方法注入依赖。
        1)通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征
        2) 如果存在满足条件的setter方法,则通过 ObjectFactory 获取依赖对象,
        3)通过反射调用 setter 方法将依赖设置到目标对象中。
    整个过程对应的代码如下:
private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
            	// 遍历目标类的所有方法
                for (Method method : instance.getClass().getMethods()) {
                	// 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
                    if (method.getName().startsWith("set")
                            && method.getParameterTypes().length == 1
                            && Modifier.isPublic(method.getModifiers())) {
                    	// 获取 setter 方法参数类型
                        Class<?> pt = method.getParameterTypes()[0];// 方法返回一个Class对象数组,它们以声明顺序表示由此Method对象表示的方法的形式参数类型
                        try {
                        	// 获取属性名,比如 setProtocol 那么property = “protocol”
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            // 从 ObjectFactory 中获取依赖对象
                            Object object = objectFactory.getExtension(pt, property);// pt:参数类型
                            if (object != null) {
                            	// 通过反射调用 setter 方法设置依赖
                                method.invoke(instance, object);// instance:需要执行method的对象,object:method方法的参数,
                            }
                        } catch (Exception e) {
                            logger.error("fail to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }

最后

以上就是内向鱼为你收集整理的dubbo SPI 的实现前言一、关于dubbo spi二、小例子三、原理分析的全部内容,希望文章能够帮你解决dubbo SPI 的实现前言一、关于dubbo spi二、小例子三、原理分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部