目录

  • 1、加载机制概述
  • 1.1 Java SPI
  • 1.2 Dubbo SPI
  • 1.3 扩展点的配置规范
  • 1.4 扩展点的分类与缓存
  • 1.5 扩展点的特性
  • 2、扩展点注解
  • 2.1 @SPI 注解
  • 2.2 @Adaptive 注解
  • 2.3 @Activate 注解
  • 3、ExtensionLoader 的工作原理
  • 3.1 getExtension的实现原理
  • 3.2 getAdaptiveExtension的实现原理
  • 3.3 getActivateExtension的实现原理
  • 4、扩展点动态编译的实现
  • 4.1 Javassist动态代码编译
  • 4.2 JDK动态代码编译


1、加载机制概述

Dubbo SPI没有直接使用Java SPI,在它的思想上又做了一定的改进,形成了一套自己的配置规范和特性,同时又兼容Java SPI

1.1 Java SPI

具体参考 Java SPI

1.2 Dubbo SPI

具体参考 Dubbo SPI

1.3 扩展点的配置规范

  Dubbo SPI和Java SPI类似,需要在META-INF/dubbo/下放置对应的SPI配置文件,文件名称需要命名为接口的全路径名。配置文件的内容为key=扩展点实现类全路径名,如果有多个实现类则使用换行符分隔。在Dubbo启动的时候,会默认扫这三个目录下的配置文件:META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/,如表所示:

规范名

规范说明

SPI配置文件路径

META-INF/services/、 META-INF/dubbo/、 META-INF/dubbo/intemal/

SPI配置文件名称

全路径类名

文件内容格式

key=value方式,多个用换行符分隔

1.4 扩展点的分类与缓存

  Dubbo SPI可以分为Class缓存、实例缓存,这两种缓存能根据扩展类的种类分为普通扩展类、包装扩展类(Wrapper类)、自适应扩展类(Adaptive类)等

  • Class缓存:Dubbo SPI获取扩展类时,会先从缓存中读取。如果缓存中不存在,则加载配置文件,根据配置把Class缓存到内存中,并不会直接全部初始化
  • 实例缓存:Dubbo框架中不仅缓存Class,也会缓存Class实例化后的对象。每次获取的时候,会先从缓存中读取,如果缓存中读不到,则重新加载并缓存起来。

1.5 扩展点的特性

  • 自动包装:ExtensionLoader在加载扩展时,如果发现这个扩展类包含其他扩展点作为构造函数的参数,则这个扩展类就会被认为是Wrapper类
  • 自动加载:如果某个扩展类是另外一个扩展点类的成员属性,并且拥有setter方法,那么框架也会自动注入对应的扩展点实例,ExtensionLoader在执行扩展点初始化的时候,会自动通过setter方法注入对应的实现类
  • 自适应:使用 @Adaptive 注解,可以动态地通过URL中的参数来确定要使用哪个具体的实现类。从而解决自动加载中的实例注入问题
  • 自动激活:使用 @Activate 注解,可以标记对应的扩展点默认被激活启用。该注解还可以通过传入不同的参数,设置扩展点在不同的条件下被自动激活

2、扩展点注解

2.1 @SPI 注解

  @SPI注解可以使用在类、接口和枚举类上,主要作用就是标记这个接口是一个Dubbo SPI接口,即是一个扩展点,可以有多个不同的内置或用户定义的实现。@SPI注解的代码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {

    // default extension name
    String value() default "";

    // scope of SPI, default value is application scope.
    ExtensionScope scope() default ExtensionScope.APPLICATION;
}

  通过value属性,以传入不同的参数来设置这个接口的默认实现类!

2.2 @Adaptive 注解

  @Adaptive 注解可以标记在类、接口、枚举类和方法上,Dubbo框架中几乎都标注在方法上,即方法级别注解,则可以通过参数动态获得实现类,方法级别注解在第一次getExtensionLoader时,会自动生成和编译一个动态的Adaptive类,从而达到动态实现类的效果。Adaptive注解的代码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {

    String[] value() default {};

}

  该注解也可以传入value参数,是一个数组。可以传入多个key值,在初始化Adaptive注解的接口时,会先对传入的URL进行key值匹配,第一个key没匹配上则匹配第二个,以此类推。直到所有的key匹配完毕,如果还没有匹配到, 则会使用“驼峰规则”匹配,如果也没匹配到,则会抛出IllegalStateException异常。

2.3 @Activate 注解

  @Activate可以标记在类、接口、枚举类和方法上。主要使用在有多个扩展点实现、需要根据不同条件被激活的场景中,Activate 注解的代码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
    
    // URL中的分组如果匹配则激活,则可以设置多个
    String[] group() default {};
	
	// 查找URL中如果含有该key值,则会激活
    String[] value() default {};

    /**
     * 表示哪些扩展点要在本扩展点之前
     * Deprecated since 2.7.0
     */
    @Deprecated
    String[] before() default {};

    /**
     * 表示哪些扩展点要在本扩展点之后
     * Deprecated since 2.7.0
     */
    @Deprecated
    String[] after() default {};

    // 整型,直接的排序信息,数值越小越先加载
    int order() default 0;
}

3、ExtensionLoader 的工作原理

ExtensionLoader是整个扩展机制的主要逻辑类,在这个类里面实现了配置的加载、扩展类缓存、自适应对象生成等所有工作。

  ExtensionLoader 的逻辑入口可以分为 getExtension、getAdaptiveExtension、getActivateExtension三个,分别是获取普通扩展类、获取自适应扩展类、获取自动激活的扩展类。ExtensionLoader 总体加载流程如下:

dubbo扩展路由 dubbo扩展点 哪些_dubbo扩展路由

3.1 getExtension的实现原理

  当调用getExtension(String name)方法时,会先检查缓存中是否有现成的数据,没有则调用createExtension开始创建,核心代码如下:

// 获取普通扩展类
public T getExtension(String name) {
    T extension = getExtension(name, true);
    if (extension == null) {
        throw new IllegalArgumentException("Not find extension: " + name);
    }
    return extension;
}

public T getExtension(String name, boolean wrap) {
    ······
    // 缓存中获取
    final Holder<Object> holder = getOrCreateHolder(cacheKey);
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            // 缓存没有则创建
            if (instance == null) {
                instance = createExtension(name, wrap);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}

private T createExtension(String name, boolean wrap) {
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null || unacceptableExceptions.contains(name)) {
        throw findException(name);
    }
    try {
        T instance = (T) extensionInstances.get(clazz);
        if (instance == null) {
            extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
            instance = (T) extensionInstances.get(clazz);
            instance = postProcessBeforeInitialization(instance, name);
            injectExtension(instance);
            instance = postProcessAfterInitialization(instance, name);
        }

        if (wrap) {
            List<Class<?>> wrapperClassesList = new ArrayList<>();
            if (cachedWrapperClasses != null) {
                wrapperClassesList.addAll(cachedWrapperClasses);
                wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                Collections.reverse(wrapperClassesList);
            }

            if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                for (Class<?> wrapperClass : wrapperClassesList) {
                    Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                    boolean match = (wrapper == null) ||
                        ((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
                            !ArrayUtils.contains(wrapper.mismatches(), name));
                    if (match) {
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        instance = postProcessAfterInitialization(instance, name);
                    }
                }
            }
        }

        // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
            type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}

  在调用createExtension开始创建的过程中,也会先检查缓存中是否有配置信息,如果不存在扩展类,则会从 META-INF/services/> META-INF/dubbo/、META-INF/dubbo/internal/这几个路径中读取所有的配置文件,通过I/O读取字符流,然后通过解析字符串,得到配置文件中对应的扩展点实现类的全称。

扩展点配置信息加载过程:

private Map<String, Class<?>> getExtensionClasses() {
    // 1、先尝试从缓存中获取classes
    Map<String, Class<?>> classes = cachedClasses.get();
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                try {
                    // 2、开始加载Class
                    classes = loadExtensionClasses();
                } catch (InterruptedException e) {
                    logger.error("Exception occurred when loading extension class (interface: " + type + ")", e);
                    throw new IllegalStateException("Exception occurred when loading extension class (interface: " + type + ")", e);
                }
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}

private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
    ······
    for (LoadingStrategy strategy : strategies) {
        // 3、加载路径下面的SPI配置文件
        loadDirectory(extensionClasses, strategy, type.getName());

        // compatible with old ExtensionFactory
        if (this.type == ExtensionInjector.class) {
            loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
        }
    }
    return extensionClasses;
}

private void loadDirectory(Map<String, Class<?>> extensionClasses, LoadingStrategy strategy, String type) throws InterruptedException {
	// 加载配置
    loadDirectoryInternal(extensionClasses, strategy, type);
    try {
        String oldType = type.replace("org.apache", "com.alibaba");
        if (oldType.equals(type)) {
            return;
        }
        //if class not found,skip try to load resources
        ClassUtils.forName(oldType);
        loadDirectoryInternal(extensionClasses, strategy, oldType);
    } catch (ClassNotFoundException classNotFoundException) {

    }
}

private void loadDirectoryInternal(Map<String, Class<?>> extensionClasses, LoadingStrategy loadingStrategy, String type) throws InterruptedException {
    String fileName = loadingStrategy.directory() + type;
    try {
        List<ClassLoader> classLoadersToLoad = new LinkedList<>();

        // try to load from ExtensionLoader's ClassLoader first
        if (loadingStrategy.preferExtensionClassLoader()) {
            ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
            if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
                classLoadersToLoad.add(extensionLoaderClassLoader);
            }
        }

        if (specialSPILoadingStrategyMap.containsKey(type)){
            String internalDirectoryType = specialSPILoadingStrategyMap.get(type);
            //skip to load spi when name don't match
            if (!LoadingStrategy.ALL.equals(internalDirectoryType)
                && !internalDirectoryType.equals(loadingStrategy.getName())){
                return;
            }
            classLoadersToLoad.clear();
            classLoadersToLoad.add(ExtensionLoader.class.getClassLoader());
        }else {
            // load from scope model
            Set<ClassLoader> classLoaders = scopeModel.getClassLoaders();

            if (CollectionUtils.isEmpty(classLoaders)) {
                // 通过 getSystemResources 得到配置文件
                Enumeration<java.net.URL> resources = ClassLoader.getSystemResources(fileName);
                if (resources != null) {
                    while (resources.hasMoreElements()) {
                        // 循环遍历urls,解析字符串,得到扩展实现类,并加入缓存
                        loadResource(extensionClasses, null, resources.nextElement(), loadingStrategy.overridden(),
                            loadingStrategy.includedPackages(),
                            loadingStrategy.excludedPackages(),
                            loadingStrategy.onlyExtensionClassLoaderPackages());
                    }
                }
            } else {
                classLoadersToLoad.addAll(classLoaders);
            }
        }

		//通过 loadResources 得到配置文件
        Map<ClassLoader, Set<java.net.URL>> resources = ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad);
        // 循环遍历urls,解析字符串,得到扩展实现类,并加入缓存
        resources.forEach(((classLoader, urls) -> {
            loadFromClass(extensionClasses, loadingStrategy.overridden(), urls, classLoader,
                loadingStrategy.includedPackages(),
                loadingStrategy.excludedPackages(),
                loadingStrategy.onlyExtensionClassLoaderPackages());
        }));
    } catch (InterruptedException e) {
        throw e;
    } catch (Throwable t) {
        logger.error("Exception occurred when loading extension class (interface: " +
            type + ", description file: " + fileName + ").", t);
    }
}

  加载完扩展点配置后,再通过反射获得所有扩展实现类并缓存起来,此处仅仅是把Class加载到JVM中,但并没有做Class初始化。扩展类的缓存分类核心代码:

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,boolean overridden) {
    ······
    if (clazz.isAnnotationPresent(Adaptive.class)) {
    	// 如果是自适应类(Adaptive )则缓存,缓存的自适应类只能有一个
        cacheAdaptiveClass(clazz, overridden);
    } else if (isWrapperClass(clazz)) {
    	// 如果是包装扩展类(Wrapper),则直接加入包装扩展类的Set集合
       if (cachedWrapperClasses == null) {
            cachedWrapperClasses = new ConcurrentHashSet<>();
        }
        cachedWrapperClasses.add(clazz);
    } else {
        if (StringUtils.isEmpty(name)) {
            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 (ArrayUtils.isNotEmpty(names)) {
            // 有自动激活注解(Activate),则缓存到自动激活的缓存中
            cacheActivateClass(clazz, names[0]);
            for (String n : names) {
                // 缓存普通扩展类
                cacheName(clazz, n);
                saveInExtensionClass(extensionClasses, clazz, n, overridden);
            }
        }
    }
}

3.2 getAdaptiveExtension的实现原理

  在getAdaptiveExtension()方法中,会为扩展点接口自动生成实现类字符串,实现类主要包含以下逻辑:为接口中每个有@Adaptive注解的方法生成默认实现(没有注解的方法则生成空实现),每个默认实现都会从URL中提取Adaptive参数值,并
以此为依据动态加载扩展点。然后,框架会使用不同的编译器,把实现类字符串编译为自适应类并返回。大致流程如下:

// 获取自适应扩展类
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
checkDestroyed();
Object instance = cachedAdaptiveInstance.get();
if (instance == 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);
            }
        }
    }
}

private T createAdaptiveExtension() {
    try {
        T instance = (T) getAdaptiveExtensionClass().newInstance();
        ········
        return instance;
    } catch (Exception e) {
        throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

private Class<?> createAdaptiveExtensionClass() {
    // Adaptive Classes' ClassLoader should be the same with Real SPI interface classes' ClassLoader
    ClassLoader classLoader = type.getClassLoader();
    try {
        if (NativeUtils.isNative()) {
            return classLoader.loadClass(type.getName() + "$Adaptive");
        }
    } catch (Throwable ignore) {

    }
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    org.apache.dubbo.common.compiler.Compiler compiler = extensionDirector.getExtensionLoader(
        org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    // 生成实现类
    return compiler.compile(type, code, classLoader);
}

  @Adaptive注解是加在实现类AdaptiveCompiler上的。这样一来AdaptiveCompiler就会作为该自适应类的默认实现,不需要再做代码生成和编译就可以使用了

3.3 getActivateExtension的实现原理

getActivateExtension(URL url, String[] values, String group)方法可以获取所有自动激活扩展点。主流程分为4步:

  • 检查缓存,如果缓存中没有,则初始化所有扩展类实现的集合
  • 遍历整个©Activate注解集合,根据传入URL匹配条件(匹配group、name等),得到所有符合激活条件的扩展类实现,然后根据@©Activate中配置的before、after、order等参数进行排序
  • 遍历所有用户自定义扩展类名称,根据用户URL配置的顺序,调整扩展点激活顺序
  • 返回所有自动激活类集合
@SuppressWarnings("deprecation")
public List<T> getActivateExtension(URL url, String[] values, String group) {
    checkDestroyed();
    // solve the bug of using @SPI's wrapper method to report a null pointer exception.
    Map<Class<?>, T> activateExtensionsMap = new TreeMap<>(activateComparator);
    List<String> names = values == null ? new ArrayList<>(0) : asList(values);
    Set<String> namesSet = new HashSet<>(names);
    // URL的参数不包含 -default 才会激活
    if (!namesSet.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
        if (cachedActivateGroups.size() == 0) {
            synchronized (cachedActivateGroups) {
                // cache all extensions
                if (cachedActivateGroups.size() == 0) {
                    getExtensionClasses();
                    for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
                        String name = entry.getKey();
                        Object activate = entry.getValue();

                        String[] activateGroup, activateValue;

                        if (activate instanceof Activate) {
                            activateGroup = ((Activate) activate).group();
                            activateValue = ((Activate) activate).value();
                        } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
                            activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
                            activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
                        } else {
                            continue;
                        }
                        cachedActivateGroups.put(name, new HashSet<>(Arrays.asList(activateGroup)));
                        String[][] keyPairs = new String[activateValue.length][];
                        for (int i = 0; i < activateValue.length; i++) {
                            if (activateValue[i].contains(":")) {
                                keyPairs[i] = new String[2];
                                String[] arr = activateValue[i].split(":");
                                keyPairs[i][0] = arr[0];
                                keyPairs[i][1] = arr[1];
                            } else {
                                keyPairs[i] = new String[1];
                                keyPairs[i][0] = activateValue[i];
                            }
                        }
                        cachedActivateValues.put(name, keyPairs);
                    }
                }
            }
        }

        // traverse all cached extensions
        cachedActivateGroups.forEach((name, activateGroup) -> {
            if (isMatchGroup(group, activateGroup)
                && !namesSet.contains(name)
                && !namesSet.contains(REMOVE_VALUE_PREFIX + name)
                && isActive(cachedActivateValues.get(name), url)) {

                activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
            }
        });
    }

    if (namesSet.contains(DEFAULT_KEY)) {
        // will affect order
        // `ext1,default,ext2` means ext1 will happens before all of the default extensions while ext2 will after them
        ArrayList<T> extensionsResult = new ArrayList<>(activateExtensionsMap.size() + names.size());
        for (String name : names) {
            if (name.startsWith(REMOVE_VALUE_PREFIX)
                || namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
                continue;
            }
            if (DEFAULT_KEY.equals(name)) {
                extensionsResult.addAll(activateExtensionsMap.values());
                continue;
            }
            if (containsExtension(name)) {
                extensionsResult.add(getExtension(name));
            }
        }
        return extensionsResult;
    } else {
        // add extensions, will be sorted by its order
        for (String name : names) {
            if (name.startsWith(REMOVE_VALUE_PREFIX)
                || namesSet.contains(REMOVE_VALUE_PREFIX + name)) {
                continue;
            }
            if (DEFAULT_KEY.equals(name)) {
                continue;
            }
            if (containsExtension(name)) {
            	// 获取Activate扩展类实现,也是通过getExtension得到的
                activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
            }
        }
        return new ArrayList<>(activateExtensionsMap.values());
    }
}

注意:如果URL的参数中传入了“-default”,则所有的默认@Activate都不会被激活,只有URL参数中指定的扩展点会被激活。如果传入了“—” 符号开头的扩展点名, 则该扩展点也不会被自动激活。

4、扩展点动态编译的实现

  动态生成的自适应类只是字符串,需要通过编译才能得到真正的Class,配合动态编译器,灵活地在原始类基础上创建新的自适应类。Dubbo中有三种代码编译器,分别是JDK编译器Javassist编译器AdaptiveCompiler编译器。编译器类之间的关系如图:

dubbo扩展路由 dubbo扩展点 哪些_dubbo_02


Compiler接口上含有一个SPI注解,注解的默认值是@SPI(”javassist”),很明显,Javassist编译器将作为默认编译器。如果想改变默认编译器,则可以通过<dubbo:application compiler=“jdk” />标签进行配置。AdaptiveCompiler上面有@Adaptive注解,说明AdaptiveCompiler会固定为默认实现,用来管理其他Compiler,AdaptiveCompiler代码如下:

@Adaptive
public class AdaptiveCompiler implements Compiler, ScopeModelAware {
    private FrameworkModel frameworkModel;

    @Override
    public void setFrameworkModel(FrameworkModel frameworkModel) {
        this.frameworkModel = frameworkModel;
    }

    private static volatile String DEFAULT_COMPILER;
	
	// 设置默认的编译器名称
	// 在 ApplicationConfig 中被调用,也就是 Dubbo在启动时,会解析配置中的<dubbo:application compiler="jdk" />标签,获取设置的值,如果没有标签设置,贝U使用@SPI(Hjavassistn)中的设置,即3avassistCompilero
    public static void setDefaultCompiler(String compiler) {
        DEFAULT_COMPILER = compiler;
    }

    @Override
    public Class<?> compile(Class<?> neighbor, String code, ClassLoader classLoader) {
        Compiler compiler;
        ExtensionLoader<Compiler> loader = frameworkModel.getExtensionLoader(Compiler.class);
        String name = DEFAULT_COMPILER; // copy reference
        if (name != null && name.length() > 0) {
            compiler = loader.getExtension(name);
        } else {
            compiler = loader.getDefaultExtension();
        }
        // 通过ExtensionLoader获取对应的编译器扩展类实现,并调用真正的compile做编译
        return compiler.compile(neighbor, code, classLoader);
    }
}

4.1 Javassist动态代码编译

  之前已经生成了代码字符串,因此在JavassistCompiler中,就是不断通过正则表达式匹配不同部位的代码,然后调用Javassist库中的API生成不同部位的代码,最后得到一个完整的Class对象。具体步骤如下:

  • 初始化Javassist,设置默认参数,如设置当前的classpath
  • 通过正则匹配出所有import的包,并使用Javassist添加import
  • 通过正则匹配出所有extends的包,创建Class对象,并使用Javassist添加extends
  • 通过正则匹配出所有implements包,并使用Javassist添加implements
  • 通过正则匹配出类里面所有内容,即得到{}中的内容,再通过正则匹配出所有方法,并使用Javassist添加类方法
  • 生成Class对象

以上步骤就是整个doCompile方法在JavassistCompiler中的实现,具体代码如下:

public class JavassistCompiler extends AbstractCompiler {

    private static final Pattern IMPORT_PATTERN = Pattern.compile("import\\s+([\\w\\.\\*]+);\n");

    private static final Pattern EXTENDS_PATTERN = Pattern.compile("\\s+extends\\s+([\\w\\.]+)[^\\{]*\\{\n");

    private static final Pattern IMPLEMENTS_PATTERN = Pattern.compile("\\s+implements\\s+([\\w\\.]+)\\s*\\{\n");

    private static final Pattern METHODS_PATTERN = Pattern.compile("\n(private|public|protected)\\s+");

    private static final Pattern FIELD_PATTERN = Pattern.compile("[^\n]+=[^\n]+;");

    @Override
    public Class<?> doCompile(Class<?> neighbor, ClassLoader classLoader, String name, String source) throws Throwable {
        CtClassBuilder builder = new CtClassBuilder();
        builder.setClassName(name);

        // process imported classes
        Matcher matcher = IMPORT_PATTERN.matcher(source);
        while (matcher.find()) {
            builder.addImports(matcher.group(1).trim());
        }

        // process extended super class
        matcher = EXTENDS_PATTERN.matcher(source);
        if (matcher.find()) {
            builder.setSuperClassName(matcher.group(1).trim());
        }

        // process implemented interfaces
        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
        CtClass cls = builder.build(classLoader);

        ClassPool cp = cls.getClassPool();
        if (classLoader == null) {
            classLoader = cp.getClassLoader();
        }
        cp.insertClassPath(new LoaderClassPath(classLoader));
        cp.insertClassPath(new DubboLoaderClassPath());

        try {
            return cp.toClass(cls, neighbor, classLoader, JavassistCompiler.class.getProtectionDomain());
        } catch (Throwable t) {
            if (!(t instanceof CannotCompileException)) {
                return cp.toClass(cls, classLoader, JavassistCompiler.class.getProtectionDomain());
            }
            throw t;
        }
    }
}

4.2 JDK动态代码编译

  JdkCompiler是Dubbo编译器的另一种实现,使用了JDK动态代码编译,整个动态编译过程可以简单地总结为:首先初始化一个JavaFileObject对象,并把代码字符串作为参数传入构造方法,然后调用JavaCompiler.CompilationTask方法编译出具体的类。JavaFileManager负责管理类文件的输入/输出位置。