zoukankan      html  css  js  c++  java
  • dubbo源码分析5——SPI机制_AdaptiveExtension的原理和作用

    private T createAdaptiveExtension() {
            try {
                return injectExtension((T) getAdaptiveExtensionClass().newInstance());
            } catch (Exception e) {
                throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
            }
        }
    private Class<?> getAdaptiveExtensionClass() {
            getExtensionClasses();
            if (cachedAdaptiveClass != null) {
                return cachedAdaptiveClass;
            }
            return cachedAdaptiveClass = createAdaptiveExtensionClass();
        }
        

    ExtensionLoader类的createAdaptiveExtension()方法在前面的  ExtensionFactory的作用 ExtensionLoader类中的objectFactory分析 都涉及到,当时因为主要是讲解IOC功能,所以没有对createAdaptiveExtension()方法进行细致分析,只是简单的把源码的执行过程梳理了下,我们现在就基于之前的分析,再来深入理解一下,先贴出以前的分析成果:

           createAdaptiveExtension() 方法执行逻辑:

    
          ->getAdaptiveExtensionClass()   //下面的调用有两个分支
    
           // *********** 分支1 *******************  在找到的类上有Adaptive注解
        ->getExtensionClasses()
          ->loadExtensionClasses()
            ->loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    
         injectExtension   //完成注入,这是 ExtensionFactory 类的作用之所在 
    
           // *********** 分支2 ******************* 在找到的类上没有Adaptive注解
           ->createAdaptiveExtensionClass()
          ->createAdaptiveExtensionClassCode() //动态生成代码
    ->ClassLoader classLoader = findClassLoader();
    ->com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); //从SPI容器中获取编译器Compiler
    ->return compiler.compile(code, classLoader); //编译出动态类
        injectExtension 

    从两个分支来看,分支1是在找在SPI接口的实现类上是否有Adaptive注解的,有的话就返回这个类的Class实例,当分支1的情况不存在的时候,会继续走分支2 。

     分支1的代码之前有分析,下面看分支2的代码,着重分析这一句代码:createAdaptiveExtensionClassCode() , 这句是生成动态类的代码以供后续的编译器去编译


    下面是源码,通过源码还能发现几个规则:
    (1)就是只有当相应的
    SPI接口的所有方法上是否有带Adaptive注解的方法,如果有就会生成动态类的代码然后进行动态编译(比如使用javassist框架),如果没有带Adaptive注解的方法 ,那就说明该SPI接口是没有Adaptive性质的实现类的,就会拋出异常
    (2)动态类的本质也是在实现相应的SPI接口,它最终也是在调一个现成的SPI实现类来工作,这样就会有这样的疑问,那为何不直接指定呢,还非得用动态的呢,呵呵,这就是为什么凡是在方法上出现Adaptive注解的SPI的Adaptive形式都要动态的原因了,因为dubbo这样一来就可以做到用不同的Adaptive方法,调不同的SPI实现类去处理。
    
    

    private String createAdaptiveExtensionClassCode() {
       StringBuilder codeBuidler = new StringBuilder();
       Method[] methods = type.getMethods();

      
    boolean hasAdaptiveAnnotation = false; for(Method m : methods) { if(m.isAnnotationPresent(Adaptive.class)) { hasAdaptiveAnnotation = true; break; } } // 完全没有Adaptive方法,则不需要生成Adaptive类 if(! hasAdaptiveAnnotation) throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");
    //动态类的包名就是相应的SPI接口的包名
    //动态类的类名就  SPI接口名+$Adpative codeBuidler.append(
    "package " + type.getPackage().getName() + ";"); codeBuidler.append(" import " + ExtensionLoader.class.getName() + ";"); codeBuidler.append(" public class " + type.getSimpleName() + "$Adpative" + " implements " + type.getCanonicalName() + " {"); for (Method method : methods) { Class<?> rt = method.getReturnType(); Class<?>[] pts = method.getParameterTypes(); Class<?>[] ets = method.getExceptionTypes(); Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); StringBuilder code = new StringBuilder(512);
    //对于SPI方法上不带Adaptive注解的方法,生成的方法代码统统在方法体内抛出UnsupportedOperationException
    if (adaptiveAnnotation == null) { code.append("throw new UnsupportedOperationException("method ") .append(method.toString()).append(" of interface ") .append(type.getName()).append(" is not adaptive method!");"); } else { int urlTypeIndex = -1; for (int i = 0; i < pts.length; ++i) { if (pts[i].equals(URL.class)) { urlTypeIndex = i; break; } } // 有类型为URL的参数 if (urlTypeIndex != -1) { // Null Point check String s = String.format(" if (arg%d == null) throw new IllegalArgumentException("url == null");", urlTypeIndex); code.append(s); s = String.format(" %s url = arg%d;", URL.class.getName(), urlTypeIndex); code.append(s); } // 参数没有URL类型 else { String attribMethod = null; // 找到参数的URL属性 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; } } } if(attribMethod == null) { throw new IllegalStateException("fail to create adative class for interface " + type.getName() + ": not found url parameter or url attribute in parameters of method " + method.getName()); } // Null point check String s = String.format(" if (arg%d == null) throw new IllegalArgumentException("%s argument == null");", urlTypeIndex, pts[urlTypeIndex].getName()); code.append(s); s = String.format(" if (arg%d.%s() == null) throw new IllegalArgumentException("%s argument %s() == null");", urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod); code.append(s); s = String.format("%s url = arg%d.%s();",URL.class.getName(), urlTypeIndex, attribMethod); code.append(s); }
    //获取方法上的Adaptive注解的value值,如果没有就以SPI接口名做为默认的value,但是会把大写字母前+".",并转小写字母这样进行处理 String[] value
    = adaptiveAnnotation.value(); // 没有设置Key,则使用“扩展点接口名的点分隔 作为Key if(value.length == 0) { char[] charArray = type.getSimpleName().toCharArray(); StringBuilder sb = new StringBuilder(128); for (int i = 0; i < charArray.length; i++) { if(Character.isUpperCase(charArray[i])) { if(i != 0) { sb.append("."); } sb.append(Character.toLowerCase(charArray[i])); } else { sb.append(charArray[i]); } } value = new String[] {sb.toString()}; } boolean hasInvocation = false; for (int i = 0; i < pts.length; ++i) { if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) { // Null Point check String s = String.format(" if (arg%d == null) throw new IllegalArgumentException("invocation == null");", i); code.append(s); s = String.format(" String methodName = arg%d.getMethodName();", i); code.append(s); hasInvocation = true; break; } }
    //cachedDefaultName就是SPI注解中的value值 String defaultExtName
    = cachedDefaultName; String getNameCode = null; for (int i = value.length - 1; i >= 0; --i) { if(i == value.length - 1) { if(null != defaultExtName) {
    //如果SPI注解提供了默认扩展名,且方法的Adaptive注解中的value有 "protocol"
                    if(!"protocol".equals(value[i])) if (hasInvocation) //方法参数中有com.alibaba.dubbo.rpc.Invocation类型的参数,则方法参数上还应有URL这样的参数 , 并从url上获取看是否有 SPI的name参数,如没有就使用SPI注解的value getNameCode = String.format("url.getMethodParameter(methodName, "%s", "%s")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter("%s", "%s")", value[i], defaultExtName); //意思类似上面,只是获取的位置不同   else //方法的Adaptive注解上的value没有"protocol" getNameCode = String.format("( url.getProtocol() == null ? "%s" : url.getProtocol() )", defaultExtName); } else { //如果SPI注解上没提供默认扩展名 if(!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, "%s", "%s")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter("%s")", value[i]); else getNameCode = "url.getProtocol()"; } } else { if(!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, "%s", "%s")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter("%s", %s)", value[i], getNameCode); else getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } code.append(" String extName = ").append(getNameCode).append(";"); // check extName == null? String s = String.format(" if(extName == null) " + "throw new IllegalStateException("Fail to get extension(%s) name from url(" + url.toString() + ") use keys(%s)");", type.getName(), Arrays.toString(value)); code.append(s); s = String.format(" %s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);", type.getName(), ExtensionLoader.class.getSimpleName(), type.getName()); code.append(s); // return statement if (!rt.equals(void.class)) { code.append(" return "); } s = String.format("extension.%s(", method.getName()); code.append(s); for (int i = 0; i < pts.length; i++) { if (i != 0) code.append(", "); code.append("arg").append(i); } code.append(");"); } codeBuidler.append(" public " + rt.getCanonicalName() + " " + method.getName() + "("); for (int i = 0; i < pts.length; i ++) { if (i > 0) { codeBuidler.append(", "); } codeBuidler.append(pts[i].getCanonicalName()); codeBuidler.append(" "); codeBuidler.append("arg" + i); } codeBuidler.append(")"); if (ets.length > 0) { codeBuidler.append(" throws "); for (int i = 0; i < ets.length; i ++) { if (i > 0) { codeBuidler.append(", "); } codeBuidler.append(pts[i].getCanonicalName()); } } codeBuidler.append(" {"); codeBuidler.append(code.toString()); codeBuidler.append(" }"); } codeBuidler.append(" }"); if (logger.isDebugEnabled()) { logger.debug(codeBuidler.toString()); } return codeBuidler.toString(); }

    下面我们再来看看,如果符合条件,这个动态生成的代码是个啥样子的( 以Protocol为例):

    public class Protocol$Adpative implements Protocol {
    
        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 <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
            if (invoker == null)
                throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
            if (invoker.getUrl() == null)
                throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
            com.alibaba.dubbo.common.URL url = invoker.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(invoker);
        }
    
        public <T> Invoker<T> refer(Class<T> type, URL url1) throws RpcException {
            if (url1 == null)
                throw new IllegalArgumentException("url == null");
            com.alibaba.dubbo.common.URL url = url1;
            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(type, url1);
        }
    
        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!");
        }
    
    }

     总结:

        1. 获取某个SPI接口的adaptive实现类的规则是:

            (1)实现类的类上面有Adaptive注解的,那么这个类就是adaptive类

            (2)实现类的类上面没有Adaptive注解,但是在方法上有Adaptive注解,则会动态生成adaptive类

        2 .生成的动态类的编译类是:com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler类

        3. 动态类的本质是可以做到一个SPI中的不同的Adaptive方法可以去调不同的SPI实现类去处理。使得程序的灵活性大大提高。这才是整套SPI设计的一个精华之所在

     
  • 相关阅读:
    973. K Closest Points to Origin
    919. Complete Binary Tree Inserter
    993. Cousins in Binary Tree
    20. Valid Parentheses
    141. Linked List Cycle
    912. Sort an Array
    各种排序方法总结
    509. Fibonacci Number
    374. Guess Number Higher or Lower
    238. Product of Array Except Self java solutions
  • 原文地址:https://www.cnblogs.com/hzhuxin/p/7561353.html
Copyright © 2011-2022 走看看