《阿布的点点滴滴》:dubbo spi机制源码分析

前言

dubbo模拟java实现了自己的spi机制,同时进行了相应的扩展,给开发者提供了扩展点。理解dubbo的spi机制,对阅读dubbo的源码有很大的帮助!

环境:dubbo : 2.6.2、JDK 1.8

配置环境:XXX为实现类

ExtensionLoader

ExtensionLoader是dubbo实现spi的核心类!

1.1 getExtension

public T getExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        //1:通过spi的name先从容器中找,找不到就创建一个Holder
        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) {
                    //2: 实例化对象
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

1.1.1 若cachedInstances容器中没有相应的对象,则调用createExtension方法去创建。下面代码为实例化对象的过程:

private T createExtension(String name) {
        //1,加载配置文件中的内容,一行行解析,存入一个Map<String,Class>的容器中
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
           //2,完成对象的实例化  class->object
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //3,自动注入 set方法,涉及Adaptive相关内容
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            //4,若wrapperClass不为空,则接口中存在包装类,需将调用包装类的有参构造方法进行实例化
            //构造参数即为上步实例化好的instance,最后返回给用户一个包装类的对象
            //完成代理的功能
            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);
        }
    }

spi实例化对象的过程:

第一步:解析配置文件中的内容,解析完成后,存入Holder<Map<String, Class<?>>> cachedClasses中;

           解析的过程:若实现类中,有参数为此接口的构造函数,则此类被认为是包装类,存入cachedWrapperClasses中;

                                若实现类中,类上有Adaptive注解,则此类视为一个自适应类,即代理类,存入变量                                 cachedAdaptiveClass。  在自动注入时,会注入自适应实现类;

                                 若实现类不是上面的两种情况,则将其加入到extensionClasses容器中,之后存入cachedClasses。

第二步:实例化对象。获取对应的class对象,并将其实例化为object对象,class->object;

第三步:set方法自动注入。遍历set方法,若参数值为spi接口,则需自动注入。

                       自动注入过程:若前期的解析中,未发现有自适应的扩展类,则系统自动生成一个自适应的扩展类。采用工厂模                   式,通过SpiExtensionFactory,调用ExtensionLoader#getAdaptiveExtension,生成自适应扩展类

第四步:若存在包装类,则需调用其参数为spi接口的构造函数,实例化对象,并完成自动注入流程。最后返回的是一个包装类。

1.1.1 自动注入,是如何生成自适应实现类的,且看以下代码:

private Class<?> createAdaptiveExtensionClass() {
        //1,以字符串形式拼接Adaptive类,
        String code = createAdaptiveExtensionClassCode();
        ClassLoader classLoader = findClassLoader();
        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        //通过javassist 将code封装成ctclass对象,再转换为字节码,生成class对象 
        return compiler.compile(code, classLoader);
}

//拼接Car$Adaptive类
private String createAdaptiveExtensionClassCode() {
        StringBuilder codeBuilder = new StringBuilder();
        ...
        ...
        codeBuilder.append("package ").append(type.getPackage().getName()).append(";");
        codeBuilder.append("\nimport ").append(ExtensionLoader.class.getName()).append(";");
        //拼接类名
        codeBuilder.append("\npublic class ").append(type.getSimpleName()).append("$Adaptive").append(" implements ").append(type.getCanonicalName()).append(" {");

        ...
        // find URL getter method
                    LBL_PTS:
                    for (int i = 0; i < pts.length; ++i) {
                        Method[] ms = pts[i].getMethods();
                        for (Method m : ms) {
                            String name = m.getName();
                            if ((name.startsWith("get") || name.length() > 3)
                                    && Modifier.isPublic(m.getModifiers())
                                    && !Modifier.isStatic(m.getModifiers())
                                    && m.getParameterTypes().length == 0
                                    && m.getReturnType() == URL.class) {
                                urlTypeIndex = i;
                                attribMethod = name;
                                break LBL_PTS;
                            }
                        }
                    }
}
   //javassit技术
 public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain)
        throws CannotCompileException
    {
        try {
            //将ctclass对象转换为字节数组
            byte[] b = ct.toBytecode();
            java.lang.reflect.Method method;
            Object[] args;
            if (domain == null) {
                method = defineClass1;
                args = new Object[] { ct.getName(), b, new Integer(0),
                                      new Integer(b.length)};
            }
            else {
                method = defineClass2;
                args = new Object[] { ct.getName(), b, new Integer(0),
                    new Integer(b.length), domain};
            }
            //生成class对象,加载到jvm内存中
            return (Class)toClass2(method, loader, args);
        }
        catch (RuntimeException e) {
            throw e;
        }
        catch (java.lang.reflect.InvocationTargetException e) {
            throw new CannotCompileException(e.getTargetException());
        }
        catch (Exception e) {
            throw new CannotCompileException(e);
        }
    }

     以上代码,先拼接好代码,再利用javassist技术来,将字符串形式的代码封装成CtClass对象,并转换成字节码,生成class对象。

在jvm内存中查看自动生成的xxx$AdaptiveClass  

可看到,dubbo为我们生成了一个Car$Adaptive的类,这就是set方法传入的对象。细看其实现方法,是通过URL作为参数来找到程序员想要注入的实现类,这就需要在定义spi接口时,方法需传入URL,且方法上需加@Adaptive注解,类似下面的接口:

(注意,若参数列表中没有URL将报错,不加@Adaptive也将报错)

而调用方法时,需构造一个URL,并传入一个map,将@Adaptive中的值作为key,配置文件中实现类的name作为value,如下:

  总结                   

dubbo的spi可做的事情:

①可完成自动加载spi接口的实现类;

②若含有包装类,可为实现类自动代理;

③可完成实现类的自动注入(setXXX); 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值