JDK-SPI-服务提供者接口

归档

SPI 源码说明

  • java.util.ServiceLoader
java 复制代码
/*** 服务加载器:给定接口,查找实现类。实现可迭代接口 */
public final class ServiceLoader<S> implements Iterable<S> {

    /*** 返回 ServiceLoader 实例 */
    public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
        return new ServiceLoader<>(service, loader);
    }

    /*** 构造器 */
    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        // 记录要查找的接口类
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        // 记录加载器
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        ...
        reload(); // (重新)加载
    }

    /*** 重新加载 */
    public void reload() {
        providers.clear(); // 清空
        lookupIterator = new LazyIterator(service, loader); // 创建懒加载迭代器
    }

    /*** 实现可迭代接口:返回迭代器 */
    @Override
    public Iterator<S> iterator() {
        /*** 一个新的迭代器实现。对懒加载迭代器 lookupIterator 进行封装 */
        return new Iterator<S>() {
            Iterator<Map.Entry<String, S>> knownProviders = providers.entrySet().iterator();
            ...
            public S next() { // 先从已加载的里面选
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next(); // 最后才开始加载
            }
            ...
        };
    }

}
  • java.util.ServiceLoader.LazyIterator
java 复制代码
    /*** 懒加载-迭代器。实现迭代器接口 */
    private class LazyIterator implements Iterator<S> {

        /*** 构造器 */
        private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }

        /*** 获取下一个服务实现对象 */
        public S next() {
            if (acc == null) {
                return nextService(); // 获取下一个服务实现对象
            } 
            ...
        }

        /*** 获取下一个服务实现对象 */
        private S nextService() {
            ...
            String cn = nextName; // 下一个服务实现类的类名。nextName 在 hasNextService() 方法里面设置
            Class<?> c = null;
            try {
                c = Class.forName(cn, false, loader); // 初始化类
            } catch (ClassNotFoundException x) {
                ...
            }
            ...
            try {
                S p = service.cast(c.newInstance()); // 实例化一个对象
                providers.put(cn, p); // 添加到提供者(providers)Map 里,方便上面的判断
                return p;
            }
            ...
        }

        /*** 判断是否有下一个服务 */
        private boolean hasNextService() {
            ...
            if (configs == null) {
                try {
                    // String PREFIX = "META-INF/services/";
                    String fullName = PREFIX + service.getName();
                    ...
                    configs = loader.getResources(fullName); // 加载所有的文件
                } 
                ...
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement()); // 逐个文件加载填充 pending
            }
            nextName = pending.next(); // 设置下一个要加载的类的类名
            return true;
        }
    }
相关推荐
界面开发小八哥35 分钟前
「Java EE开发指南」如何用MyEclipse创建一个WEB项目?(三)
java·ide·java-ee·myeclipse
idolyXyz1 小时前
[java: Cleaner]-一文述之
java
一碗谦谦粉1 小时前
Maven 依赖调解的两大原则
java·maven
netyeaxi2 小时前
Java:使用spring-boot + mybatis如何打印SQL日志?
java·spring·mybatis
收破烂的小熊猫~2 小时前
《Java修仙传:从凡胎到码帝》第四章:设计模式破万法
java·开发语言·设计模式
猴哥源码2 小时前
基于Java+SpringBoot的动物领养平台
java·spring boot
老任与码2 小时前
Spring AI Alibaba(1)——基本使用
java·人工智能·后端·springaialibaba
小兵张健2 小时前
武汉拿下 23k offer 经历
java·面试·ai编程
FreeBuf_2 小时前
Apache组件遭大规模攻击:Tomcat与Camel高危RCE漏洞引发数千次利用尝试
java·tomcat·apache
无妄-20242 小时前
软件架构升级中的“隐形地雷”:版本选型与依赖链风险
java·服务器·网络·经验分享