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;
        }
    }
相关推荐
用户128526116026 小时前
我把祖传Java项目重构后,接口响应从3s砍到了200ms,只改了这几行代码
java
Linsk6 小时前
组件 = 模板 + 业务逻辑
java·前端·vue.js
星沉远浦7 小时前
用Gemini高效解决Java代码报错难以定位的问题
java
用户2986985301411 小时前
Word 文档字符级格式化:Java 实现方案详解
java·后端
笨鸟飞不快11 小时前
从单个服务到集群:一次完整的性能排查复盘
java·前端
荣码11 小时前
用Streamlit给AI应用套个界面,10行代码出Web页面
java·python
SamDeepThinking11 小时前
Java微服务练习方式
java·后端·微服务
朦胧之1 天前
AI 编程-老项目改造篇
java·前端·后端
程序猿大帅1 天前
别再只当调包侠了:用 Spring AI 落地 Function Calling,我被大模型硬生生砸出了三个大坑
java
程序员晓琪1 天前
约定大于配置:基于 Java 包名自动生成 API 版本路由的最佳实践
java·spring boot·后端