Java的SPI机制

SPI(Service Provider Interface),是JDK内置的一种 服务提供发现机制.可以为某个接口寻找服务实现.

demo:

vbnet 复制代码
public class TestSpi {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        ServiceLoader<SpiInterface> load = ServiceLoader.load(SpiInterface.class);
        Iterator<SpiInterface> iterator = load.iterator();
        if (iterator.hasNext()) {
            SpiInterface next = iterator.next();
            next.spiInterFaceMethod("hello");
        }
    }
}

接口:

arduino 复制代码
public interface SpiInterface {
    
    public List<String> spiInterFaceMethod(String keyword);
}

SPI实现类:

typescript 复制代码
public class FileSearch implements SpiInterface {
    @Override
    public List<String> spiInterFaceMethod(String keyword) {
        System.out.println("测试SPI机制");
        return null;
    }
}

在resources目录下创建META-INF/services路径.然后再以包路径创建一个文件.

执行结果:

SPI分析:

load方法:

xml 复制代码
    /**
     * Creates a new service loader for the given service type, using the
     * current thread's {@linkplain java.lang.Thread#getContextClassLoader
     * context class loader}.
     *
     * <p> An invocation of this convenience method of the form
     *
     * <blockquote><pre>
     * ServiceLoader.load(<i>service</i>)</pre></blockquote>
     *
     * is equivalent to
     *
     * <blockquote><pre>
     * ServiceLoader.load(<i>service</i>,
     *                    Thread.currentThread().getContextClassLoader())</pre></blockquote>
     *
     * @param  <S> the class of the service type
     *
     * @param  service
     *         The interface or abstract class representing the service
     *
     * @return A new service loader
     */
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

load方法分析:

vbnet 复制代码
    /**
     * Creates a new service loader for the given service type and class
     * loader.
     *
     * @param  <S> the class of the service type
     *
     * @param  service
     *         The interface or abstract class representing the service
     *
     * @param  loader
     *         The class loader to be used to load provider-configuration files
     *         and provider classes, or <tt>null</tt> if the system class
     *         loader (or, failing that, the bootstrap class loader) is to be
     *         used
     *
     * @return A new service loader
     */
    public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }

构造了一个ServiceLoader对象.

ini 复制代码
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;
        reload();
    }

构造方法理解:

reload方法:

csharp 复制代码
    /**
     * Clear this loader's provider cache so that all providers will be
     * reloaded.
     *
     * <p> After invoking this method, subsequent invocations of the {@link
     * #iterator() iterator} method will lazily look up and instantiate
     * providers from scratch, just as is done by a newly-created loader.
     *
     * <p> This method is intended for use in situations in which new providers
     * can be installed into a running Java virtual machine.
     */
    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

reload方法就是构造了一个叫做LazyIterator的迭代器对象.

hasNext方法:

typescript 复制代码
public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

hasNextService方法:

ini 复制代码
    private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }

hasNextService方法理解:

next方法:

csharp 复制代码
   public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

nextService方法:

typescript 复制代码
  private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                S p = service.cast(c.newInstance());
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

nextService方法理解:

这就是SPI机制的一个流程.

类加载器的cast方法:

SPI机制的缺陷:

1.不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费.

2.获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类.

3.多个并发多线程使用 ServiceLoader 类的实例是不安全的.

语雀地址www.yuque.com/itbosunmian...?

《Go.》 密码:xbkk 欢迎大家访问.提意见.

相关推荐
用户298698530142 小时前
Java 实现两个 Word 文档的差异比对
java·后端
用户6757049885022 小时前
再见 pip!Rust 写的 uv 正在把 Python 包管理按在地上摩擦
后端·python
用户6757049885022 小时前
Python 装饰器很难?那是你没看到这篇文章!
后端·python
小瓦码J码2 小时前
轻量化线程池实战:忙时并发、闲时归零,搞定周期批量任务
java·后端
irving同学462382 小时前
Drizzle ORM + PostgreSQL + Hono 学习笔记
前端·后端
irving同学462382 小时前
TypeScript 后端入门全景:Hono + Zod + Drizzle + PostgreSQL
前端·后端
百珏2 小时前
[灰度发布]:灰度流量如何匹配与识别:从特征补全到网关命中引擎
java·后端·架构
Reart2 小时前
csapp 第三章 3.2.2 x86-64 函数调用约定:寄存器分工与c语言转汇编学习(未完成版)
后端·计算机组成原理
dinl_vin2 小时前
FastAPI 系列(一)· 初体验——从 Spring Boot 工程师视角认识 FastAPI
后端·python·fastapi