【ShenYu系列】ShenYu的SPI实现源码分析

前言

前面我已经介绍【面试系列】详细拆解Java、Spring、Dubbo三者SPI机制的原理,当已经有了合适的实现,shenyu自身的SPI和上面的有啥区别,值得玩味。

什么是SPI

SPI就是Service Provider Interface,直译"服务提供方接口",是一种动态的服务发现机制,可以基于接口运行时动态加载接口的实现类(也就是接口编程 + 策略模式 + 配置文件的一种开发模式)。最常见的就是JDK内置的数据库驱动接口java.sql.Driver,不同的厂商可以对该接口完成不同的实现,例如MySQLMySQL驱动包中的com.mysql.jdbc.Driver)、PostgreSQLPostgreSQL驱动包中的org.postgresql.Driver)等等。

ShenYu的SPI

Demo

JdbcSPI jdbcSPI = ExtensionLoader.getExtensionLoader(JdbcSPI.class).getJoin("mysql");

源码

ExtensionLoader#getExtensionLoader(java.lang.Class),判断类是否是接口,是否存在@SPI注解。

    public static <T> ExtensionLoader<T> getExtensionLoader(final Class<T> clazz) {
        return getExtensionLoader(clazz, ExtensionLoader.class.getClassLoader());
    }

    public static <T> ExtensionLoader<T> getExtensionLoader(final Class<T> clazz, final ClassLoader cl) {
        
        Objects.requireNonNull(clazz, "extension clazz is null");
        
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException("extension clazz (" + clazz + ") is not interface!");
        }
        if (!clazz.isAnnotationPresent(SPI.class)) {
            throw new IllegalArgumentException("extension clazz (" + clazz + ") without @" + SPI.class + " Annotation");
        }
        ExtensionLoader<T> extensionLoader = (ExtensionLoader<T>) LOADERS.get(clazz);
        if (Objects.nonNull(extensionLoader)) {
            return extensionLoader;
        }
        LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz, cl));
        return (ExtensionLoader<T>) LOADERS.get(clazz);
    }

ExtensionLoader,构造方法。获取ExtensionFactory

    private ExtensionLoader(final Class<T> clazz, final ClassLoader cl) {
        this.clazz = clazz;
        this.classLoader = cl;
        if (!Objects.equals(clazz, ExtensionFactory.class)) {
            ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getExtensionClassesEntity();
        }
    }

ExtensionLoader#getExtensionClassesEntity,获取本地缓存,本地缓存不存在,加载类信息。

    private Map<String, ClassEntity> getExtensionClassesEntity() {
        Map<String, ClassEntity> classes = cachedClasses.getValue();
        if (Objects.isNull(classes)) {
            synchronized (cachedClasses) {
                classes = cachedClasses.getValue();
                if (Objects.isNull(classes)) {
                    classes = loadExtensionClass();
                    cachedClasses.setValue(classes);
                    cachedClasses.setOrder(0);
                }
            }
        }
        return classes;
    }

ExtensionLoader#loadExtensionClass,加载类信息主要是加载指定目录下面的文件

    private Map<String, ClassEntity> loadExtensionClass() {
        SPI annotation = clazz.getAnnotation(SPI.class);
        if (Objects.nonNull(annotation)) {
            String value = annotation.value();
            if (StringUtils.isNotBlank(value)) {
                cachedDefaultName = value;
            }
        }
        Map<String, ClassEntity> classes = new HashMap<>(16);
        loadDirectory(classes);
        return classes;
    }

ExtensionLoader#loadDirectory,加载目录下的文件

    private void loadDirectory(final Map<String, ClassEntity> classes) {
        String fileName = SHENYU_DIRECTORY + clazz.getName();
        try {
            Enumeration<URL> urls = Objects.nonNull(this.classLoader) ? classLoader.getResources(fileName)
                    : ClassLoader.getSystemResources(fileName);
            if (Objects.nonNull(urls)) {
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    loadResources(classes, url);
                }
            }
        } catch (IOException t) {
            LOG.error("load extension class error {}", fileName, t);
        }
    }

ExtensionLoader#loadResources,加载资源文件

    private void loadResources(final Map<String, ClassEntity> classes, final URL url) throws IOException {
        try (InputStream inputStream = url.openStream()) {
            Properties properties = new Properties();
            properties.load(inputStream);
            properties.forEach((k, v) -> {
                String name = (String) k;
                String classPath = (String) v;
                if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(classPath)) {
                    try {
                        loadClass(classes, name, classPath);
                    } catch (ClassNotFoundException e) {
                        throw new IllegalStateException("load extension resources error", e);
                    }
                }
            });
        } catch (IOException e) {
            throw new IllegalStateException("load extension resources error", e);
        }
    }

ExtensionLoader#loadClass,判断是否是实现类,是否存在Join注解,满足条件,封装成ClassEntity,添加到classes中。

    private void loadClass(final Map<String, ClassEntity> classes,
                           final String name, final String classPath) throws ClassNotFoundException {
        Class<?> subClass = Objects.nonNull(this.classLoader) ? Class.forName(classPath, true, this.classLoader) : Class.forName(classPath);
        if (!clazz.isAssignableFrom(subClass)) {
            throw new IllegalStateException("load extension resources error," + subClass + " subtype is not of " + clazz);
        }
        if (!subClass.isAnnotationPresent(Join.class)) {
            throw new IllegalStateException("load extension resources error," + subClass + " without @" + Join.class + " annotation");
        }
        ClassEntity oldClassEntity = classes.get(name);
        if (Objects.isNull(oldClassEntity)) {
            Join joinAnnotation = subClass.getAnnotation(Join.class);
            ClassEntity classEntity = new ClassEntity(name, joinAnnotation.order(), subClass, joinAnnotation.isSingleton());
            classes.put(name, classEntity);
        } else if (!Objects.equals(oldClassEntity.getClazz(), subClass)) {
            throw new IllegalStateException("load extension resources error,Duplicate class " + clazz.getName() + " name "
                    + name + " on " + oldClassEntity.getClazz().getName() + " or " + subClass.getName());
        }
    }

ExtensionLoader#getJoin,创建对应的实例。

    public T getJoin(final String name) {
        if (StringUtils.isBlank(name)) {
            throw new NullPointerException("get join name is null");
        }
        Holder<Object> objectHolder = cachedInstances.get(name);
        if (Objects.isNull(objectHolder)) {
            cachedInstances.putIfAbsent(name, new Holder<>());
            objectHolder = cachedInstances.get(name);
        }
        Object value = objectHolder.getValue();
        if (Objects.isNull(value)) {
            synchronized (cachedInstances) {
                value = objectHolder.getValue();
                if (Objects.isNull(value)) {
                    createExtension(name, objectHolder);
                    value = objectHolder.getValue();
                    if (!objectHolder.isSingleton()) {
                        Holder<Object> removeObj = cachedInstances.remove(name);
                        removeObj = null;
                    }
                }
            }
        }
        return (T) value;
    }

ExtensionLoader#createExtension,根据接口信息去找到配置文件,从配置文件中读取实现类。

    private void createExtension(final String name, final Holder<Object> holder) {
        ClassEntity classEntity = getExtensionClassesEntity().get(name);
        if (Objects.isNull(classEntity)) {
            throw new IllegalArgumentException(name + "name is error");
        }
        Class<?> aClass = classEntity.getClazz();
        Object o = joinInstances.get(aClass);
        if (Objects.isNull(o)) {
            try {
                if (classEntity.isSingleton()) {
                    joinInstances.putIfAbsent(aClass, aClass.newInstance());
                    o = joinInstances.get(aClass);
                } else {
                    o = aClass.newInstance();
                }
            } catch (InstantiationException | IllegalAccessException e) {
                throw new IllegalStateException("Extension instance(name: " + name + ", class: "
                        + aClass + ")  could not be instantiated: " + e.getMessage(), e);
                
            }
        }
        holder.setOrder(classEntity.getOrder());
        holder.setValue(o);
        holder.setSingleton(classEntity.isSingleton());
    }

配置文件如下

mysql=org.apache.shenyu.spi.fixture.MysqlSPI
oracle=org.apache.shenyu.spi.fixture.OracleSPI
canNotInstantiated=org.apache.shenyu.spi.fixture.CanNotInstantiatedSPI

总结一下

Shenyu的spi,更多像是dubbo的spi简化版。springboot是key=a,b,c,不涉及对应关系。dubbo是类名作为文件名,key获取实现类的主键。相比于dubbo,简化了adaptive等功能。

你可能感兴趣的:(开源项目介绍,java)