rrpc:引入简单spi机制实现主动发现某种服务

一、概述

SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。可以用来启用框架扩展和替换组件, 主要是被框架的开发人员使用

比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现, MySQL和PostgreSQL都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。Java中SPI机制 主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是解耦。

SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。

SPI整体机制图如下: 

将目录结构按照如下的方式建立,并按照规范填写响应的内容即可完成spi的自动发现。

文件结构主要是 文件名(实现的接口名称) + 文件内容(code+type具体实现类的权限定名)

code 要和 我们之前实现的工厂内置的code一致

 

 二、实现步骤

1、在Configuration配置类中加载spi机制

  public Configuration() {
        //1.成员变量的默认配置项

        //2.spi 机制发现相关配置项
        SpiResolver spiResolver = new SpiResolver();
        spiResolver.loadFromSpi(this);

        //3.读取xml获取上边的信息
        XmlResolver xmlResolver = new XmlResolver();
        xmlResolver.loadFromXml(this);

        //4.编程配置项:rrpcbootstarp提供
    }

 2、SpiHandler

实现spi机制的主要代码,用于获取和解析services下的文件,并创建缓存,避免重复读取文件内容

 * 实现一个简易版本的spi
 */
@Slf4j
public class SpiHandler {
    //创建spi缓存,保存spi相关的原始内存
    private static final Map<String, List<String>> SPI_CONTENT = new ConcurrentHashMap<>(8);
    //保存我们的spi文件
    private static final String BASE_PATH = "META-INF/rrpc-services";

    //创建实例的缓存
    public static final Map<Class<?>, List<ObjectWrapper<?>>> SPI_IMPLEMENT = new ConcurrentHashMap<>(8);

    //加载当前工程的jar包中的classPath资源,加载后将spi信息保存,避免重复io
    static {
        //1.使用classLoader类加载机制进行加载 PATH路径下的文件
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL fileUrl = classLoader.getResource(BASE_PATH);
        if (fileUrl != null) {
            //获取到的文件是多个 用数组的方式去保存
            File file = new File(fileUrl.getPath());
            File[] children = file.listFiles();
            if (children != null && children.length > 0) {
                for (File child : children) {
                    String key = child.getName();
                    List<String> value = getImplNames(child);
                    SPI_CONTENT.put(key, value);
                }
            }
        }
    }


    /**
     * 获取第一个和当前服务相关的实例
     *
     * @param clazz 一个服务接口的class实例
     * @param <T>
     * @return
     */
    public synchronized static <T> ObjectWrapper<T> get(Class<T> clazz) {
        //1.优先读取缓存,缓存中没有的话再创建实例
        List<ObjectWrapper<?>> objectWrappers = SPI_IMPLEMENT.get(clazz);
        if (objectWrappers != null && objectWrappers.size() > 0) {
            return (ObjectWrapper<T>) objectWrappers.get(0);
        }
        //2.构建缓存
        buildCache(clazz);

        List<ObjectWrapper<?>> result = SPI_IMPLEMENT.get(clazz);
        if (result == null || result.size() == 0) {
            return null;
        }

        //3.再次尝试获取第一个
        return (ObjectWrapper<T>) result.get(0);
    }

    /**
     * 获取所有和当前服务相关的实例
     *
     * @param clazz 一个服务接口的class实例
     * @param <T>
     * @return 实现类的实例集合
     * synchronized用于线程安全的保护,避免拿不到而且修改变量的情况
     */
    public synchronized static <T> List<ObjectWrapper<T>> getList(Class<T> clazz) {
        //1.优先读取缓存,缓存中没有的话再创建实例
        List<ObjectWrapper<?>> objectWrappers = SPI_IMPLEMENT.get(clazz);
        if (objectWrappers != null && objectWrappers.size() > 0) {
            //通过lamda表达式对集合的每个元素进行处理
            return objectWrappers.stream().map(wrapper -> (ObjectWrapper<T>) wrapper)
                    .collect(Collectors.toList());

        }
        //2.构建缓存
        buildCache(clazz);

        //3.再次获取
        objectWrappers = SPI_IMPLEMENT.get(clazz);
        //通过lamda表达式对集合的每个元素进行处理
        if (objectWrappers != null && objectWrappers.size() > 0) {
            return objectWrappers.stream().map(wrapper -> (ObjectWrapper<T>) wrapper)
                    .collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

        /**
         * 构建clazz相关的缓存
         * @param clazz 一个类的clazz实例
         */
        private static void buildCache (Class < ? > clazz){
            //1.通过clazz获取与之匹配的实现名称
            String name = clazz.getName();
            List<String> implNames = SPI_CONTENT.get(name);
            if (implNames == null || implNames.size() == 0) {
                return;
            }

            //2.实例化所有的实现
            List<ObjectWrapper<?>> impls = new ArrayList<>();
            //通过反射的创建实例
            for (String implName : implNames) {
                try {
                    //首先进行分割
                    String[] codeAndTypeAndName = implName.split("-");
                    if (codeAndTypeAndName.length != 3){
                        throw new SpiExcecption("您配置的spi文件不合法");
                    }
                    //将三个元素取出
                    Byte code = Byte.valueOf(codeAndTypeAndName[0]);
                    String type = codeAndTypeAndName[1];
                    String implementName = codeAndTypeAndName[2];

                    Class<?> aClass = Class.forName(implementName);
                    Object impl = aClass.getConstructor().newInstance();

                    ObjectWrapper objectWrapper = new ObjectWrapper<>(code,type,impl);

                    impls.add(objectWrapper);
                } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
                         IllegalAccessException |
                         InvocationTargetException e) {
                    log.error("实例化【{}】的实现时发生异常", implName, e);
                }

            }
            SPI_IMPLEMENT.put(clazz, impls);

        }
        /**
         * 工具类 获取文件内所有的实现名称
         * @param child 文件对象
         * @return 实现类的权限定名称
         */
        private static List<String> getImplNames (File child){
            //读取文件中的内容
            try (
                    FileReader fileReader = new FileReader(child);
                    //使用装饰器设计模式 将fileReader装饰成 BufferedReader
                    BufferedReader bufferedReader = new BufferedReader(fileReader);
            ) {
                List<String> lineNames = new ArrayList<>();
                while (true) {
                    String line = bufferedReader.readLine();
                    if (line == null) break;
                    lineNames.add(line);
                }
                return lineNames;

            } catch (IOException e) {
                log.error("读取spi文件时发生异常。。", e);
            }
            return null;
        }
        
    }

 3、SpiResolver

用于配置上下文,通过spi的方式加载配置项

public class SpiResolver {

    /**
     * 通过spi的方式加载配置项
     * @param configuration 配置上下文
     */
    public void loadFromSpi(Configuration configuration) {

        //通过spi文件,获取一个LoadBalance实现
        List<ObjectWrapper<LoadBalancer>> loadBalanceWrapper = SpiHandler.getList(LoadBalancer.class);
        //将其放入工厂
        if (loadBalanceWrapper != null && loadBalanceWrapper.size() > 0){
            configuration.setLoadBalancer(loadBalanceWrapper.get(0).getImpl());
        }

        List<ObjectWrapper<Compressor>> objectWrappers = SpiHandler.getList(Compressor.class);
        if (objectWrappers != null) {
            objectWrappers.forEach(CompressorFactory::addCompressor);
        }

        List<ObjectWrapper<Serializer>> serializerObjectWrappers = SpiHandler.getList(Serializer.class);
        if (serializerObjectWrappers != null) {
            serializerObjectWrappers.forEach(SerializerFactory::addSerializer);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北方569

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值