dubbo是可扩展的,让整个框架的接口和具体实现完全解偶。

1,java SPI

Java的扩展机制,SPI采用的是策略模式,一个接口可以多个实现类,我这里就写了一个

首先写个接口和接口实现类

//接口
public interface PrintService {


    void printInfo();
}


//接口实现类
public class PrintServiceImpl implements PrintService {

    @Override
    public void printInfo() {
        System.out.println("hello world");
    }
}

然后main方法准备起来

/**
     * java的SPI
     * @param args
     */
    public static void main(String[] args) {
        ServiceLoader<PrintService> loader = ServiceLoader.load(PrintService.class);
        //循环调用方法,因为一个接口可以多个实现类
        for (PrintService p : loader) {
            p.printInfo();
        }
    }
ServiceLoader这个类是在java.util中,这个就是javaSPI的核心,源码:java.util.ServiceLoader;自己看哈

当然还差一个东西,看了源码你会发现,ServiceLoader会去读取一个配置文件的:

private static final String PREFIX = "META-INF/services/";

所以在resoources里新建文件夹META-INF/services/,在文件夹里新建一个文件,文件名是你接口的全路径:

com.dubbo.study.utils.test.PrintService

这个文件里的内容是对应的实现类:com.dubbo.study.utils.test.PrintServiceImpl

Java支持扩展类么 java扩展点和扩展_dubbo

然后执行main方法,就会调用接口啦,打印出hello world

总结:看过程是不是很熟悉,很想调用接口,这跟我们平时用spring的调用service接口,spring是将接口加载为一个bean,然后注入,这里其实也差不多,通过META-INF/services/文件里的配置,然后用ServiceLoader去加载接口,当然SPI作用是用于java的扩展插件,

2,dubbo SPI

dubbo也是为了可以扩展,灵活性高,采用了javaSPI的思想,当然做了一些改进,

小例子:

首先还是接口与接口实现类,实现类不用改,接口里加个注解:

@SPI("print")//加了一个SPI注解,这是dubbo的
public interface PrintService {


    void printInfo();
}

resources里的META-INF新建dubbo文件夹,看上面图,com.dubbo.study.utils.test.PrintService文件里内容是:

print=com.dubbo.study.utils.test.PrintServiceImpl

print对应的就是注解里的内容。

然后执行

/**
     * dubbo的SPI
     * @param args
     */
    public static void main(String[] args) {
        PrintService p = ExtensionLoader.getExtensionLoader(PrintService.class).getDefaultExtension();
        p.printInfo();
    }

也可以调用到接口,打印出hello world

这里dubbo的ExtensionLoader类,这个类的源码在org.apache.dubbo.common.extension.ExtensionLoader,

在源码里可以看到

private static final String SERVICES_DIRECTORY = "META-INF/services/";
    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
    private static final String DUBBO_INTERNAL_DIRECTORY = "META-INF/dubbo/internal/";

在META-INF里只要有这三个文件夹,把接口的配置文件放在这三个文件夹里都可以。

配置文件的内容采用key=value形式。

dubboSPI可以分为class缓存,实例缓存。

private final ExtensionFactory objectFactory;
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap();
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder();
    private final Map<String, Object> cachedActivates = new ConcurrentHashMap();
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap();
    private final Holder<Object> cachedAdaptiveInstance = new Holder();

扩展点的特性:自动包装,自动加载,自适应,自动激活

当然这些特性除了@SPI注解是不够的,还要有@Adaptive(自适应),@Activate(自动激活)。

@SPi刚刚已经演示过用法了

@Adaptive

@SPI("print")
public interface PrintService {

    @Adaptive({"print",""})
    void printInfo();
}

注解里值是配置文件对应的key,可以配置多个。

源码在和ExtensionLoader一样的包里,可以自己去多看看

@Activate

可以标记在方法,类,接口等等,

3,ExtensionLoader源码解析

Java支持扩展类么 java扩展点和扩展_缓存_02

3.1 getExtension(String name) 是整个扩展加载器中最核心的方法,源码表示看不大懂,哈哈哈哈哈

public T getExtension(String name) {
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        } else if ("true".equals(name)) {
            //如果传入true,就加载默认扩展类
            return this.getDefaultExtension();
        } else {
            //判断缓存里有没有
            Holder<Object> holder = this.getOrCreateHolder(name);
            Object instance = holder.get();
            if (instance == null) {
                synchronized(holder) {
                    instance = holder.get();
                    if (instance == null) {
                        //没有的话就新建
                        instance = this.createExtension(name);
                        holder.set(instance);
                    }
                }
            }

            return instance;
        }
    }


//判断缓存里有没有
 private Holder<Object> getOrCreateHolder(String name) {
        Holder<Object> holder = (Holder)this.cachedInstances.get(name);
        if (holder == null) {
            this.cachedInstances.putIfAbsent(name, new Holder());
            holder = (Holder)this.cachedInstances.get(name);
        }

        return holder;
    }



private T createExtension(String name) {
        Class<?> clazz = (Class)this.getExtensionClasses().get(name);
        if (clazz == null) {
            throw this.findException(name);
        } else {
            try {
                T instance = EXTENSION_INSTANCES.get(clazz);
                if (instance == null) {
                    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                    instance = EXTENSION_INSTANCES.get(clazz);
                }

                this.injectExtension(instance);
                Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
                Class wrapperClass;
                if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                    for(Iterator var5 = wrapperClasses.iterator(); var5.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
                        wrapperClass = (Class)var5.next();
                    }
                }

                return instance;
            } catch (Throwable var7) {
                throw new IllegalStateException("Extension instance (name: " + name + ", class: " + this.type + ") couldn't be instantiated: " + var7.getMessage(), var7);
            }
        }
    }
       //这个方法去加载
    private T injectExtension(T instance) {
        try {
            if (this.objectFactory != null) {
                Method[] var2 = instance.getClass().getMethods();
                int var3 = var2.length;

                for(int var4 = 0; var4 < var3; ++var4) {
                    Method method = var2[var4];
                    if (this.isSetter(method) && method.getAnnotation(DisableInject.class) == null) {
                        Class<?> pt = method.getParameterTypes()[0];
                        if (!ReflectUtils.isPrimitives(pt)) {
                            try {
                                String property = this.getSetterProperty(method);
                                Object object = this.objectFactory.getExtension(pt, property);
                                if (object != null) {
                                    method.invoke(instance, object);
                                }
                            } catch (Exception var9) {
                                logger.error("Failed to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + var9.getMessage(), var9);
                            }
                        }
                    }
                }
            }
        } catch (Exception var10) {
            logger.error(var10.getMessage(), var10);
        }

        return instance;
    }

实现了一个完整的普通扩展类加载过程,加载的每一步,都会先检查缓存中是否已经存在所需要的数据,如果存在就直接返回,没有就加载,初始化分4步:

1)读取SPI对应路径下的配置,并加载缓存

2)根据传入的名称初始化对应的扩展类

3)尝试查找符合条件的包装类

4)返回对应的扩展类实例

3.2 getAdaptiveExtension

public T getAdaptiveExtension() {
        Object instance = this.cachedAdaptiveInstance.get();
        if (instance == null) {
            if (this.createAdaptiveInstanceError != null) {
                throw new IllegalStateException("Failed to create adaptive instance: " + this.createAdaptiveInstanceError.toString(), this.createAdaptiveInstanceError);
            }

            synchronized(this.cachedAdaptiveInstance) {
                instance = this.cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        instance = this.createAdaptiveExtension();
                        this.cachedAdaptiveInstance.set(instance);
                    } catch (Throwable var5) {
                        this.createAdaptiveInstanceError = var5;
                        throw new IllegalStateException("Failed to create adaptive instance: " + var5.toString(), var5);
                    }
                }
            }
        }

        return instance;
    }

在这个方法中,会为扩展点接口自动生成实现类字符串,为接口中每个有@Adaptive注解的方法生成默认实现,每个默认实现都会从url中提取adaptive参数值,并以此为依据动态加载扩展点,然后根据不同的编译器,把实现类字符串编译为自适应类并返回。

4,ExtensionFactory原理

ExtensionLoader是核心,那ExtensionLoader是怎么加载的呢,答案是通过ExtensionFactory工厂创建的,

//工厂接口
@SPI
public interface ExtensionFactory {
    <T> T getExtension(Class<T> var1, String var2);
}




//AdaptiveExtension实现类,默认实现。
//除了这个还有另外SpiExtensionFactory和springExtensionFactory工厂。
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList();
        Iterator var3 = loader.getSupportedExtensions().iterator();

        while(var3.hasNext()) {
            String name = (String)var3.next();
            list.add(loader.getExtension(name));
        }

        this.factories = Collections.unmodifiableList(list);
    }

    public <T> T getExtension(Class<T> type, String name) {
        Iterator var3 = this.factories.iterator();

        Object extension;
        do {
            if (!var3.hasNext()) {
                return null;
            }

            ExtensionFactory factory = (ExtensionFactory)var3.next();
            extension = factory.getExtension(type, name);
        } while(extension == null);

        return extension;
    }
}




//SpiExtensionFactory工厂
public class SpiExtensionFactory implements ExtensionFactory {
    public SpiExtensionFactory() {
    }

    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()) {
                //可以看到最终还是回答AdaptiveExtension
                return loader.getAdaptiveExtension();
            }
        }

        return null;
    }
}

5,动态编译

三种编译器,jdk编译器,javassist编译器和AdaptiveCompiler编译器

Java支持扩展类么 java扩展点和扩展_java_03

 AdaptiveCompiler编译器:

package org.apache.dubbo.common.compiler.support;

import org.apache.dubbo.common.compiler.Compiler;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionLoader;

@Adaptive
public class AdaptiveCompiler implements Compiler {
    private static volatile String DEFAULT_COMPILER;

    public AdaptiveCompiler() {
    }
    
    //设置默认编译器名称
    public static void setDefaultCompiler(String compiler) {
        DEFAULT_COMPILER = compiler;
    }

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

 JavassistCompiler编译器

package org.apache.dubbo.common.compiler.support;

import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javassist.CtClass;

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]+;");

    public JavassistCompiler() {
    }
    
    //前面一系列操作都是用正则匹配文件的import,类名,方法名,等等,
    public Class<?> doCompile(String name, String source) throws Throwable {
        CtClassBuilder builder = new CtClassBuilder();
        builder.setClassName(name);
        Matcher matcher = IMPORT_PATTERN.matcher(source);

        while(matcher.find()) {
            builder.addImports(matcher.group(1).trim());
        }

        matcher = EXTENDS_PATTERN.matcher(source);
        if (matcher.find()) {
            builder.setSuperClassName(matcher.group(1).trim());
        }

        matcher = IMPLEMENTS_PATTERN.matcher(source);
        if (matcher.find()) {
            String[] ifaces = matcher.group(1).trim().split("\\,");
            Arrays.stream(ifaces).forEach((i) -> {
                builder.addInterface(i.trim());
            });
        }

        String body = source.substring(source.indexOf(123) + 1, source.length() - 1);
        String[] methods = METHODS_PATTERN.split(body);
        String className = ClassUtils.getSimpleClassName(name);
        Arrays.stream(methods).map(String::trim).filter((m) -> {
            return !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);
            }

        });
//最后去加载这个类
        ClassLoader classLoader = org.apache.dubbo.common.utils.ClassUtils.getCallerClassLoader(this.getClass());
        CtClass cls = builder.build(classLoader);
        return cls.toClass(classLoader, JavassistCompiler.class.getProtectionDomain());
    }
}

jdk编译器

jdk编译器就复杂了,位置跟javassist一样,自己看把,