美文网首页工作生活Dubbo 框架
dubbo原理:SPI机制(二)

dubbo原理:SPI机制(二)

作者: aix91 | 来源:发表于2019-07-04 18:44 被阅读0次

    在上一篇:SPI机制(一)中研究了Dubbo SPI的自适应原理;SPI机制(二)中我们来研究下Dubbo SPI是如何实现IOC的。

    1. 起点:测试用例

    我们还是从测试用例来开始分析。

    • SPI 接口
    @SPI("injection")
    public interface InjectExt {
        String echo(String msg);
    }
    
    • 接口实现(为了简洁代码,删除了get方法)
    public class InjectExtImpl implements InjectExt {
        private SimpleExt simpleExt;
        private SimpleExt simpleExt1;
        private Object genericType;
        public void setSimpleExt(SimpleExt simpleExt) {
            this.simpleExt = simpleExt;
        }
    //    @DisableInject
        public void setSimpleExt1(SimpleExt simpleExt1) {
            this.simpleExt1 = simpleExt1;
        }
        public void setGenericType(Object genericType) {
            this.genericType = genericType;
        }
    }
    
    • 测试用例
    {
        InjectExt injectExt = ExtensionLoader.getExtensionLoader(InjectExt.class).getExtension("injection");
        InjectExtImpl injectExtImpl = (InjectExtImpl) injectExt;
        Assertions.assertNotNull(injectExtImpl.getSimpleExt());
        Assertions.assertNull(injectExtImpl.getSimpleExt1());
        Assertions.assertNull(injectExtImpl.getGenericType());
    }
    

    2. getExtension

    getExtensionLoader在SPI机制(一)中有分析,就是拿到与InjectExt相关的ExtensionLoader,这里就不赘述了。直接来到getExtension方法。

    public T getExtension(String name) {
        //校验name
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
         }
        if ("true".equals(name)) {
           return getDefaultExtension();
        }
        ...
        instance = createExtension(name);
        return instance;
    }
    

    3. createExtension

    private T createExtension(String name) {
        //找到name对应的类;getExtensionClasses在上一章已经分析过,就是去load spi 配置文件,将key-value(Class<?>) 存进map中。
        Class<?> clazz = getExtensionClasses().get(name);
        // 根据clazz 创建instance :省略
        ...
        // 为创建的实例,注入属性值
        injectExtension(instance);
        ...
        return instance;
    }
    

    4. injectExtension

    根据类里的set方法,来加载其他的自适应类,然后通过反射的方式给属性附值。

    private T injectExtension(T instance) {
       
        if (objectFactory != null) {
            // 获取类下面的所有方法
            for (Method method : instance.getClass().getMethods()) {
                if (isSetter(method)) {//判断是否是set方法
                    //如果set方法被标注为DisableInject,则跳过自动注入
                    if (method.getAnnotation(DisableInject.class) != null) {
                        continue;
                    }
                    // 判断set方法的参数,如果是基本类型,跳过自动注入。
                    Class<?> pt = method.getParameterTypes()[0];
                    if (ReflectUtils.isPrimitives(pt)) {
                        continue;
                    }
                    //获取set方法参数里的自适应类,并通过反射的方式调用set方法,完成属性的注入
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                        method.invoke(instance, object);
                    }                
                }
            }
        }
    }
    

    5. objectFactory

    我们进入objectFactory的getExtension方法,看它是如何创建属性的自适应类。

    • a. getExtension
    public <T> T getExtension(Class<T> type, String name) {
            // 这里的factories是在spi文件中,指定的所有的ExtensionFactory的实现类
            for (ExtensionFactory factory : factories) {
                T extension = factory.getExtension(type, name);
                // 找到了实现类,就立即返回
                if (extension != null) {
                    return extension;
                }
            }
            return null;
        }
    

    这里的factories 是在创建AdaptiveExtensionFactory的时候,初始化的

    public AdaptiveExtensionFactory() {
        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);
      }
    

    再来看看getSupportedExtensions方法

    public Set<String> getSupportedExtensions() {
        Map<String, Class<?>> clazzes = getExtensionClasses();
        return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet()));
    }
    

    在前面的分析中,我们知道getExtensionClasses只会去保存那些没有被Adaptive标记的实现类。在ExtensionFactory的SPI 配置文件中,只定义了两个
    ExtensionFactory 类:

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

    由于AdaptiveExtensionFactory被Adaptive标记,那么在getAdaptiveExtention的时候会默认返回AdaptiveExtensionFactory;另外的SpiExtensionFactory会保存factories中。

    • b. SpiExtensionFactory.getExtention
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
     }
    

    最后,通过getAdaptiveExtension生成SimpleExt的自适应类。

    6. 回到injectExtension方法

    在injectionExtension方法中,生成了自适应的Object,然后通过反射的方式,将自适应的SimpleExt注入到InjectExtImpl中。
    注意,这里只是注入了自适应类,真正的实现要在SimpleExt属性值调用方法时,才确定。

    7. 测试

    • 测试1: 在一个非Adaptive类里面注入Adaptive类
      注意使用的是getExtension("injection")
    {
       InjectExt injectExt = ExtensionLoader.getExtensionLoader(InjectExt.class).getExtension("injection");
       InjectExtImpl injectExtImpl = (InjectExtImpl) injectExt;
       Map<String, String> map = new HashMap<String, String>();
       map.put("simple.ext", "impl2");
       URL url = new URL("p1", "1.2.3.4", 1010, "path1", map);
       String echo = injectExtImpl.getSimpleExt().echo(url, "haha");
       assertEquals("Ext1Impl2-echo", echo);
    }
    
    • 测试2:在一个Adaptive类里面注入Adaptive类
      !!!不行!!!。所以还是老实用getExtension而不是getAdaptiveExtension。
      因为在自动注入的原理中,要调用setXXX方法。如果要想在Adaptive类中使用setXXX,必须得在接口中设置该方法为@Adaptive,还要在setXXX方法中添加URL参数,否则生成的Adaptive类中的setXXX方法会抛出异常。因此不能进行注入了。

    8. SPI总结

    • 两个核心的注解:@Adaptive,@SPI. 得弄清楚两者的含义:@SPI只是指明该接口支持SPI的方式实现;@Adaptive,用在实现类上时,Dubbo在获取接口的自适应类时,就不会再使用AdaptiveClassCodeGenerator去生成自适应类了,而是直接返回标注了@Adaptive的类。用在方法上时,自适应的类要根据方法的URL参数中在运行时,决定生成哪一个实例。
    • getExtension(name): 直接根据name去生成实例,是确定的
    • getAdaptiveExtension: 生成自适应类

    相关文章

      网友评论

        本文标题:dubbo原理:SPI机制(二)

        本文链接:https://www.haomeiwen.com/subject/tsbocctx.html