• dubbo源码阅读之自适应扩展


    自适应扩展机制

    刚开始看代码,其实并不能很好地理解dubbo的自适应扩展机制的作用,我们不妨先把代码的主要逻辑过一遍,梳理一下,在了解了代码细节之后,回过头再来思考自适应扩展的作用,dubbo为什么要设计这样一种扩展机制??它的优点在什么地方??

    Adaptive注解

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface Adaptive {
    /**
     * Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
     * in the URL, and the parameter names are given by this method.
     * 该值决定了哪个扩展类会被自动注入。目标扩展名由传入的URL中的参数和Adaptive注解中的value值共同决定。
     * value相当于一些key值,用这些key值从URL中获取相应的value值,最终决定扩展名。
     * <p>
     * If the specified parameters are not found from {@link URL}, then the default extension will be used for
     * dependency injection (specified in its interface's {@link SPI}).
     * 如果传入的URL中没有符合条件的扩展名,那么使用默认扩展名(默认扩展名由SPI注解的value决定)
     * <p>
     * For examples, given <code>String[] {"key1", "key2"}</code>:
     * 例如,value的值是:String[] {"key1", "key2"}
     * <ol>
     * <li>find parameter 'key1' in URL, use its value as the extension's name</li>
     * 首先在URL中查找key1,如果存在使用key1作为扩展名
     * <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
     * 如果URL中找不到key1,查找key2,如果能找到使用key2作为扩展名
     * <li>use default extension if 'key2' doesn't appear either</li>
     * 如果key2也找不到,那么使用默认扩展名
     * <li>otherwise, throw {@link IllegalStateException}</li>
     * 如果默认扩展名为空,则抛异常
     * </ol>
     * If default extension's name is not give on interface's {@link SPI}, then a name is generated from interface's
     * class name with the rule: divide classname from capital char into several parts, and separate the parts with
     * dot '.', for example: for {@code org.apache.dubbo.xxx.YyyInvokerWrapper}, its default name is
     * <code>String[] {"yyy.invoker.wrapper"}</code>. This name will be used to search for parameter from URL.
     * 如果SPI注解中没有给出默认扩展名,就会通过如下规则生成默认扩展名:将驼峰式风格的接口名转换成.号分隔的名称作为扩展名,例如
     * 接口名org.apache.dubbo.xxx.YyyInvokerWrapper会被转换成yyy.invoker.wrapper作为扩展名。用这个名称在URL参数中查找。
     *
     *
     * @return parameter key names in URL
     */
    String[] value() default {};
    

    }

    getAdaptiveExtension

    我们先从获取自适应扩展类的入口方法看起,

    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("Failed to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }
    
        return (T) instance;
    }
    

    主要逻辑就是检查缓存,如果缓存不错存在则加载自适应扩展类,使用双检查锁机制保证在多线程的情况下不会重复加载。都是惯用法,没什么可说的。

    createAdaptiveExtension

    private T createAdaptiveExtension() {
        try {
            //1. 获取自适应扩展类并实例化
            //2. 自动注入属性值
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
    

    这里使用了dubbo实现的IOC功能,ExtensionLoader的IOC特性我们再上一节已经说过,通过ExtensionFactory接口的扩展类实现对属性的自动注入,不再赘述。
    我们的重点还是放在如何生成自适应扩展类上,即getAdaptiveExtensionClass的逻辑。

    getAdaptiveExtensionClass

    private Class<?> getAdaptiveExtensionClass() {
        //首先查找所有 资源文件,加载全部扩展类,但并未实例化
        // 在加载扩展类的过程中,如果遇到带Adaptive注解的类,会把该类设到缓存cachedAdaptiveClass
        //这种情况,由用户来实现自适应扩展的逻辑,比较简单
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        //大部分情况下,自适应扩展的逻辑不是由用户实现,而是由框架自动生成的,生成的逻辑就在下面的方法中
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }
    

    没啥好说的,继续看下一个方法

    createAdaptiveExtensionClass

    //重点来了,这个方法定义了生成自适应扩展类代码的总体方案,
    //这个方法引出了自适应扩展机制的核心类AdaptiveClassCodeGenerator
    private Class<?> createAdaptiveExtensionClass() {
        //AdaptiveClassCodeGenerator类是关键代码
        String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
        ClassLoader classLoader = findClassLoader();
        //Compiler接口也是通过dubbo自己的spi机制加载的,
        // Compiler接口的自适应扩展类是预先写好的代码,即AdaptiveCompiler
        // 这里有一个有意思的问题,如果compiler的自适应扩展类也由框架自动生成,那么这里就会出现循环调用
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        //返回编译后的Class对象
        return compiler.compile(code, classLoader);
    }
    

    AdaptiveClassCodeGenerator.generate

    生成自适应扩展类代码的核心逻辑被封装在AdaptiveClassCodeGenerator类中,AdaptiveClassCodeGenerator的构造方法仅仅设置了要扩展的接口类型以及默认扩展类名(可能为空),我们直接从generate方法看起,

    /**
     * generate and return class code
     * 生成自适应扩展类代码
     */
    public String generate() {
        // no need to generate adaptive class since there's no adaptive method found.
        // 如果接口中没有带Adaptive注解的方法,说名该类型不需要自适应扩展,直接抛异常
        if (!hasAdaptiveMethod()) {
            throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
        }
    
        StringBuilder code = new StringBuilder();
        // 生成package信息,与接口的包名相同
        code.append(generatePackageInfo());
        //生成import信息, 生成了如下的import语句
        //import org.apache.dubbo.common.extension.ExtensionLoader
        code.append(generateImports());
        //生成类定义信息
        // public class <接口名>$Adaptive implements <接口全限定名> {
        code.append(generateClassDeclaration());
    
        //生成方法信息,这一步是重点
        Method[] methods = type.getMethods();
        for (Method method : methods) {
            code.append(generateMethod(method));
        }
        //最后在末尾加上一个花括号
        code.append("}");
        
        if (logger.isDebugEnabled()) {
            logger.debug(code.toString());
        }
        return code.toString();
    }
    

    总体来看,逻辑还是比较简单的,就是我们平时写代码的步骤,依次生成包信息,import内容,类的具体代码包括每个方法的实现代码。
    其中比较重要的是生成方法的实现代码,我们重点看一下这段代码。

    generateMethod

    private String generateMethod(Method method) {
        // 方法的返回类型
        String methodReturnType = method.getReturnType().getCanonicalName();
        // 方法名称
        String methodName = method.getName();
        // 方法体,即方法的实现代码,这一步最关键
        String methodContent = generateMethodContent(method);
        // 方法参数,
        // (ParamType0 arg0, ParamType1 arg1, ParamType2 arg2)
        String methodArgs = generateMethodArguments(method);
        // 异常信息,throws内容
        String methodThrows = generateMethodThrows(method);
        //最后按照java代码的规范拼接成一个完整的方法代码
        return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent);
    }
    

    重点是生成方法具体实现代码的逻辑,generateMethodContent方法。

    generateMethodContent

    生成方法体的逻辑。这个方法比较长,我们分开来看。

    /**
     * generate method content
     * 生成方法体
     */
    private String generateMethodContent(Method method) {
        Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
        // 这一句应该放到else分支内部
        StringBuilder code = new StringBuilder(512);
        // 如果方法不带Adaptive注解,就直接生成抛Unsupported异常的实现,
        // 也就是说,自适应扩展机制必须要在需要自适应的方法上加Adaptive注解
        if (adaptiveAnnotation == null) {
            return generateUnsupported(method);
        } else {
            // 找到URL类型的参数的下标
            int urlTypeIndex = getUrlTypeIndex(method);
    
            // found parameter in URL type
            if (urlTypeIndex != -1) {
                // Null Point check
                // 如果方法参数列表中有URL类型的参数,那么添加对参数做非空检查的代码
                code.append(generateUrlNullCheck(urlTypeIndex));
            } else {
                // did not find parameter in URL type
                // 如果方法参数中没有URL类型,那么查找能够简介获取到URL的参数,这个参数类型应该含有返回类型的是URL的方法
                code.append(generateUrlAssignmentIndirectly(method));
            }
    
            // 获取Adaptive注解的value
            // 如果Adaptive注解的value为空,获取会麻烦一些,将接口的驼峰式名称转换成.分隔的名称
            // 如LoadBalance转换成load.balance
            String[] value = getMethodAdaptiveValue(adaptiveAnnotation);
    
            // 检查Invocation类型的参数
            boolean hasInvocation = hasInvocationArgument(method);
    
            // 生成检查Invocation类型参数非空的代码段,
            // 只检查第一个参数??Invocation类型的参数只会有一个??
            code.append(generateInvocationArgumentNullCheck(method));
    
            // 生成获取扩展名的代码段,
            // 扩展名就是资源文件中以k-v存储的key值
            code.append(generateExtNameAssignment(value, hasInvocation));
            // check extName == null?
            // 生成扩展名非空检查语句
            code.append(generateExtNameNullCheck(value));
    
            // 获取到扩展名后,就可以生成扩展类的获取语句了
            code.append(generateExtensionAssignment());
    
            // return statement
            // 最后加上函数调用和返回语句
            code.append(generateReturnAndInovation(method));
        }
    
        return code.toString();
    }
    

    生成的方法体大致分为三块

    • 获取URL的值。如果参数中有URL类型直接赋值;如果没有就间接获取,查找参数中包含返回URL类型的方法,调用这个方法。此外加上一些非空判断语句。
    • 获取扩展名。这一步比较复杂,要考虑的情况比较多。如果Adaptive注解中有protocol就要调用URL的getProtocol方法;用以Adaptive的value数组中的key值依次去URL中获取参数,直到获取到不为空的参数。加上扩展名的非空检查语句。
    • 通过ExtensionLoader获取扩展名对应的扩展类,赋值给变量extension
    • 最后调用扩展类的相应方法,如果返回值不是void就加上return。

    其中获取扩展名代码的生成逻辑较为复杂,我们看一下这段代码。

    generateExtNameAssignment

    private String generateExtNameAssignment(String[] value, boolean hasInvocation) {
        // TODO: refactor it
        String getNameCode = null;
        // 从后向前遍历可选的扩展名
        for (int i = value.length - 1; i >= 0; --i) {
            // 对于最后一个value值做如下处理
            if (i == value.length - 1) {
                if (null != defaultExtName) {
                    // 如果不是protocol
                    if (!"protocol".equals(value[i])) {
                        if (hasInvocation) {
                            // 如果存在Invocation参数,生成获取方法参数的代码段
                            getNameCode = String.format("url.getMethodParameter(methodName, "%s", "%s")", value[i], defaultExtName);
                        } else {
                            getNameCode = String.format("url.getParameter("%s", "%s")", value[i], defaultExtName);
                        }
                    } else {
                        // 如果是protocol,生成扩区protocol的代码段
                        getNameCode = String.format("( url.getProtocol() == null ? "%s" : url.getProtocol() )", defaultExtName);
                    }
                } 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")", value[i]);
                        }
                    } else {
                        getNameCode = "url.getProtocol()";
                    }
                }
                //对于不是最后一个的value值做如下处理
            } 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);
                }
            }
        }
    
        // 拼接成赋值语句
        // String extName = %s;
        return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode);
    }
    

    代码生成示例

    这样光看代码很难对这块代码有直观的认识,理解也不够深刻,既然这段代码的作用主要是生成代码,那么我们就定义一个自己的接口,并实现一个扩展类,用AdaptiveClassCodeGenerator来生成一下,看生成的代码到底是什么样子。

    • 首先是接口

        package org.apache.dubbo.common.extension.adaptive.codeGen;
      
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.common.extension.Adaptive;
        import org.apache.dubbo.common.extension.SPI;
        
        @SPI("default")
        public interface MyExtension {
        
        @Adaptive
        String adaptiveMethod(String param1, int param2, URL url) throws Exception;
        
        @Adaptive("protocol")
        String adaptiveMethod2(String param1, int param2, URLGetter urlGetter) throws Exception;
        
        void noAdaptiveMethod(String param1, URLGetter urlGetter) throws Exception;
        }
      
    • 实现类

        package org.apache.dubbo.common.extension.adaptive.codeGen.impl;
      
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension;
        import org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter;
        
        public class DefaultMyExtension implements MyExtension {
            @Override
            public String adaptiveMethod(String param1, int param2, URL url) {
                return String.format("Params passed in are: param1:%s, param2:%d, url:%s");
            }
        
            @Override
            public String adaptiveMethod2(String param1, int param2, URLGetter urlGetter) throws Exception {
                return String.format("Params passed in are: param1:%s, param2:%d, urlGetter:%s");
            }
        
            @Override
            public void noAdaptiveMethod(String param1, URLGetter urlGetter) throws Exception {
        
            }
        }
      
    • URL获取类。为了测试简介获取URL

        package org.apache.dubbo.common.extension.adaptive.codeGen;
        
        import org.apache.dubbo.common.URL;
        
        public class URLGetter {
            private URL url;
        
            public URLGetter(URL url){
                this.url=url;
            }
        
            public URL getUrl() {
                return url;
            }
        
            public void setUrl(URL url) {
                this.url = url;
            }
        
            @Override
            public String   toString() {
                return "URLGetter{" +
                        "url=" + url +
                        '}';
            }
        }
      
    • 测试类

        package org.apache.dubbo.common.extension.adaptive.codeGen;
        
        import org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator;
        import org.junit.jupiter.api.Test;
        
        public class AdaptiveClassCodeGeneratorTest {
        
            @Test
            public void testProtocolCodeGen() {
                AdaptiveClassCodeGenerator generator=
                        new AdaptiveClassCodeGenerator(MyExtension.class,"default");
                String code=generator.generate();
                System.out.println(code);
            }
        }
      
    • 生成的代码。也就是自适应扩展类

        package org.apache.dubbo.common.extension.adaptive.codeGen;
        
        import org.apache.dubbo.common.extension.ExtensionLoader;
        
        public class MyExtension$Adaptive implements org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension {
            public java.lang.String adaptiveMethod(java.lang.String arg0, int arg1, org.apache.dubbo.common.URL arg2) throws java.lang.Exception {
                if (arg2 == null) throw new IllegalArgumentException("url == null");
                org.apache.dubbo.common.URL url = arg2;
                // Adaptive注解中没有value,用接口名称自动生成的key值
                String extName = url.getParameter("my.extension", "default");
                if (extName == null)
                    throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) name from url (" + url.toString() + ") use keys([my.extension])");
                org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension extension = (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension.class).getExtension(extName);
                return extension.adaptiveMethod(arg0, arg1, arg2);
            }
        
            public java.lang.String adaptiveMethod2(java.lang.String arg0, int arg1, org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter arg2) throws java.lang.Exception {
                if (arg2 == null)
                    throw new IllegalArgumentException("org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter argument == null");
                if (arg2.getUrl() == null)
                    throw new IllegalArgumentException("org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter argument getUrl() == null");
                // 间接获取URL
                org.apache.dubbo.common.URL url = arg2.getUrl();
                // 如果有protocol的key,那么调用URL.getProtocol方法获取协议名称
                String extName = (url.getProtocol() == null ? "default" : url.getProtocol());
                if (extName == null)
                    throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) name from url (" + url.toString() + ") use keys([protocol])");
                org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension extension = (org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension) ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension.class).getExtension(extName);
                return extension.adaptiveMethod2(arg0, arg1, arg2);
            }
        
            // 为标Adaptive注解的方法直接抛异常
            public void noAdaptiveMethod(java.lang.String arg0, org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter arg1) throws java.lang.Exception {
                throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension.noAdaptiveMethod(java.lang.String,org.apache.dubbo.common.extension.adaptive.codeGen.URLGetter) throws java.lang.Exception of interface org.apache.dubbo.common.extension.adaptive.codeGen.MyExtension is not adaptive method!");
            }
        }
      

    这样看是不是直观多了。回过头再去看一遍代码生成逻辑,就好懂多了。

    编译加载

    代码生成之后,还需要将代码编译为字节码并通过类加载器加载到虚拟机中,获取Class对象,然后才能使用。
    负责编译的是org.apache.dubbo.common.compiler.Compiler接口,这个接口的实现类也是通过ExtensionLoader进行加载的,见ExtensionLoader.createAdaptiveExtensionClass方法。
    我们看一下Compiler接口

    Compiler

    // 默认扩展名是javassist
    @SPI("javassist")
    public interface Compiler {
    
        /**
         * Compile java source code.
         *
         * @param code        Java source code
         * @param classLoader classloader
         * @return Compiled class
         */
        Class<?> compile(String code, ClassLoader classLoader);
    
    }
    

    另外,我们再看一下dubbo-common模块中META-INF/dubbo/inetrnal/org.apache.dubbo.common.compiler.Compiler文件中的内容:

    adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler
    jdk=org.apache.dubbo.common.compiler.support.JdkCompiler
    javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler
    

    AdaptiveCompiler类上带有Adaptive注解,所以ExtensionLoader在加载时AdaptiveCompiler类会被设置为自适应扩展类。而在createAdaptiveExtensionClass方法中真是通过Compiler接口的自适应扩展类来进行编译。那么我们看一下AdaptiveCompiler中的逻辑。

    AdaptiveCompiler

    public Class<?> compile(String code, ClassLoader classLoader) {
        Compiler compiler;
        ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
        // 可以通过设置静态变量DEFAULT_COMPILER指定默认扩展类
        String name = DEFAULT_COMPILER; // copy reference
        if (name != null && name.length() > 0) {
            compiler = loader.getExtension(name);
        } else {
            // 如果未指定,则获取SPI注解指定的默认扩展类
            compiler = loader.getDefaultExtension();
        }
        return compiler.compile(code, classLoader);
    }
    

    由Compiler接口上的SPI注解,我们知道默认的实现类是JavassistCompiler,所以我们看一下这个类是怎么进行编译的。

    JavassistCompiler

    JavassistCompiler继承自AbstractCompiler,有一部分逻辑在AbstractCompiler类中,所以我们先看一下

    AbstractCompiler.compile

    private static final Pattern PACKAGE_PATTERN = Pattern.compile("package\s+([$_a-zA-Z][$_a-zA-Z0-9\.]*);");
    
    private static final Pattern CLASS_PATTERN = Pattern.compile("class\s+([$_a-zA-Z][$_a-zA-Z0-9]*)\s+");    
    
    public Class<?> compile(String code, ClassLoader classLoader) {
        //去除两端空格
        code = code.trim();
        // 匹配包名
        Matcher matcher = PACKAGE_PATTERN.matcher(code);
        String pkg;
        if (matcher.find()) {
            pkg = matcher.group(1);
        } else {
            pkg = "";
        }
        // 匹配类名
        matcher = CLASS_PATTERN.matcher(code);
        String cls;
        if (matcher.find()) {
            cls = matcher.group(1);
        } else {
            throw new IllegalArgumentException("No such class name in " + code);
        }
        // 根据包名和类名拼接类的全限定名
        String className = pkg != null && pkg.length() > 0 ? pkg + "." + cls : cls;
        try {
            // 尝试直接加载???类的字节码已经存在??或者类已经被加载过了??
            // 什么情况下会出现这种情况??
            return Class.forName(className, true, ClassHelper.getCallerClassLoader(getClass()));
        } catch (ClassNotFoundException e) {
            // 感觉没必要多这一句吧??
            // 代码合法性检查应该在生成代码的方法中做,这地方单独做一个花括号检查没什么意义吧?
            if (!code.endsWith("}")) {
                throw new IllegalStateException("The java code not endsWith "}", code: 
    " + code + "
    ");
            }
            try {
                // 由子类实现
                return doCompile(className, code);
            } catch (RuntimeException t) {
                throw t;
            } catch (Throwable t) {
                throw new IllegalStateException("Failed to compile class, cause: " + t.getMessage() + ", class: " + className + ", code: 
    " + code + "
    , stack: " + ClassUtils.toString(t));
            }
        }
    }
    

    主要就是拼接处类的全限定名,然后做一些检查,尝试直接加载类。真正的编译过程交由子类实现。

    JavassistCompiler.doCompile

    @Override
    public Class<?> doCompile(String name, String source) throws Throwable {
        // 实际执行编译的类
        CtClassBuilder builder = new CtClassBuilder();
        // 设置类名
        builder.setClassName(name);
    
        // process imported classes
        // 匹配出所有的import语句,并设置到CtClassBuilder对象中
        Matcher matcher = IMPORT_PATTERN.matcher(source);
        while (matcher.find()) {
            builder.addImports(matcher.group(1).trim());
        }
        
        // process extended super class
        // 匹配出extends的类型,设置到CtClassBuilder对象中
        matcher = EXTENDS_PATTERN.matcher(source);
        if (matcher.find()) {
            builder.setSuperClassName(matcher.group(1).trim());
        }
        
        // process implemented interfaces
        // 匹配出implements的类型,设置到CtClassBuilder对象中
        matcher = IMPLEMENTS_PATTERN.matcher(source);
        if (matcher.find()) {
            String[] ifaces = matcher.group(1).trim().split("\,");
            Arrays.stream(ifaces).forEach(i -> builder.addInterface(i.trim()));
        }
        
        // process constructors, fields, methods
        // 添加方法和域
        String body = source.substring(source.indexOf('{') + 1, source.length() - 1);
        String[] methods = METHODS_PATTERN.split(body);
        String className = ClassUtils.getSimpleClassName(name);
        Arrays.stream(methods).map(String::trim).filter(m -> !m.isEmpty()).forEach(method-> {
            if (method.startsWith(className)) {
                builder.addConstructor("public " + method);
            } else if (FIELD_PATTERN.matcher(method).matches()) {
                builder.addField("private " + method);
            } else {
                builder.addMethod("public " + method);
            }
        });
        
        // compile
        ClassLoader classLoader = ClassHelper.getCallerClassLoader(getClass());
        // 进行编译,实际的编译过程设计到字节码操作,由javassist库实现,这里不再深入下去
        CtClass cls = builder.build(classLoader);
        return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
    }
    

    实际的编译过程设计到字节码操作,由javassist库实现,这里不再深入下去。

    总结

    自适应扩展模块主要分为两块,一是生成自适应扩展类的代码,二是将代码进行编译并加载编译后的字节码。

    • 其中dubbo实现的主要逻辑就是代码生成。
      自适应加载扩展类的主要思路就是从方法的参数中获取URL参数,可以是方法参数本省,也可以从参数的类型的方法中获取;
      然后从URL参数中查找扩展名(查找的范围通过Adaptive注解的value值和SPI注解中的默认值组成),找到扩展名后再由ExtensionLoader根据扩展名加载对应的扩展类,
      然后调用对应扩展类的对应方法并返回。

    • 编译代码的逻辑主要有javassist库实现,dubbo在javassist库基础上进行了一些简单封装,大部分的逻辑都是直接调用javassist库,这部分不再深入下去,如果对java代码编译感兴趣可以细看。

  • 相关阅读:
    kafka 支持发布订阅
    linux 安装 ORACLE JDK 8
    SPRING 集成 KAFKA 发送消息
    安装kafka 集群 步骤
    zookeeper 单机集成部署
    CAS 界面根据不同的域名显示不同的界面
    POSTMAN 数据关联
    Google Code Jam 2014 Round 1B Problem B
    hdu3555
    hdu2089
  • 原文地址:https://www.cnblogs.com/zhuge134/p/10800475.html
Copyright © 2020-2023  润新知