• Java中的SPI扩展机制(有demo)


    参考连接:https://www.jianshu.com/p/3a3edbcd8f24

    一、什么是SPI

    SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

    二、设计的目的

    肯定是为了扩展性,在不想修改源码的情况下,去替换系统原有的实现,代价最小也最灵活。

    三、案例demo

    先看看目录接结构

    file

    • spi-api模块:定义扩展接口
    • spi-client模块:扩展的模块
    • spi-test模块:测试模块

    github地址:https://github.com/XiaoBinNumberOne/java-spi-demo

    四、源码分析

    首先看看ServiceLoader.java类结构
    file

    ServiceLoader.load,load方法创建了一些属性,重要的是实例化了内部类,LazyIterator。最后返回ServiceLoader的实例。

    public final class ServiceLoader<S> implements Iterable<S>
        private ServiceLoader(Class<S> svc, ClassLoader cl) {
            //要加载的接口
            service = Objects.requireNonNull(svc, "Service interface cannot be null");
            //类加载器
            loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
            //访问控制器
            acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
            //先清空
            providers.clear();
            //实例化内部类 
            LazyIterator lookupIterator = new LazyIterator(service, loader);
        }
    }
    

    查找实现类和创建实现类的过程,都在LazyIterator完成。当我们调用iterator.hasNext和iterator.next方法的时候,实际上调用的都是LazyIterator的相应方法。

    public Iterator<S> iterator() {
        return new Iterator<S>() {
            public boolean hasNext() {
                return lookupIterator.hasNext();
            }
            public S next() {
                return lookupIterator.next();
            }
            .......
        };
    }
    

    所以,我们重点关注lookupIterator.hasNext()方法,它最终会调用到hasNextService。

    private class LazyIterator implements Iterator<S>{
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null; 
        private boolean hasNextService() {
            //第二次调用的时候,已经解析完成了,直接返回
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                //META-INF/services/ 加上接口的全限定类名,就是文件服务类的文件
                //META-INF/services/com.viewscenes.netsupervisor.spi.SPIService
                String fullName = PREFIX + service.getName();
                //将文件路径转成URL对象
                configs = loader.getResources(fullName);
            }
            while ((pending == null) || !pending.hasNext()) {
                //解析URL文件对象,读取内容,最后返回
                pending = parse(service, configs.nextElement());
            }
            //拿到第一个实现类的类名
            nextName = pending.next();
            return true;
        }
    }
    
    分享学习是一件开心事
  • 相关阅读:
    vuejs cli3 env配置文件实践指南
    Nginx的rewrite(地址重定向)剖析
    什么是TCP粘包?怎么解决这个问题
    windows bat批处理语法简析
    BAT文件语法和技巧(bat文件的编写及使用)
    Asyncio之EventLoop笔记
    python struct的使用例子
    redis慢查询笔记
    redis基础操作概念等笔记
    Python实现Dijkstra算法
  • 原文地址:https://www.cnblogs.com/hy-xiaobin/p/12204849.html
Copyright © 2020-2023  润新知