java之SPI

q1871901600 发布于 2024-09-28 15 次阅读


在Java的编程世界中,有时我们需要为某个接口提供多种实现,并且在运行时动态地加载这些实现。为了实现这种解耦与插件化的设计,Java提供了java.util.ServiceLoader类。本文将深入探讨ServiceLoader的用途、运行原理与实现原理,并通过一个实战Demo来加深对它的理解。

一、用途与使用场景

ServiceLoader的主要用途是加载和实例化服务提供者,这里的“服务”通常指的是实现了某个接口的类。使用场景包括但不限于插件式架构、框架扩展、第三方库集成等。在这些场景中,核心代码定义了服务接口,而服务提供者(第三方或用户自定义)则实现了这些接口。通过ServiceLoader,核心代码可以在运行时动态地加载和使用这些服务提供者,而无需在编译时知道它们的存在。

使用场景包括但不限于:

插件式架构:当需要支持多种插件时,可以使用ServiceLoader动态加载这些插件。

框架扩展:框架开发者可以定义一些接口,允许使用者通过实现这些接口来扩展框架的功能。

第三方库集成:当需要集成多个第三方库时,可以使用ServiceLoader来加载这些库的实现类。

二、运行原理与实现原理

ServiceLoader的运行原理基于Java的SPI(Service Provider Interface)机制。SPI允许第三方为某个接口提供实现,并将这些实现类配置在特定的目录下。ServiceLoader通过扫描这些目录,找到并加载实现类,然后实例化它们。

具体实现原理如下:

查找配置文件:ServiceLoader在类路径(classpath)下的META-INF/services目录中查找以接口全限定名命名的文件。例如,如果接口名为com.example.MyService,则ServiceLoader会查找名为com.example.MyService的文件。

解析配置文件:找到文件后,ServiceLoader会读取文件内容。文件内容是一系列实现类的全限定名,每行一个。这些实现类就是服务提供者。

加载与实例化:对于每个在配置文件中找到的实现类名,ServiceLoader使用Java的类加载机制加载这些类,并创建它们的实例。这些实例被缓存在ServiceLoader内部,供后续使用。

懒加载机制:ServiceLoader采用懒加载的方式,即只有在首次调用iterator.hasNext()方法时才开始加载和实例化服务提供者。这种设计可以提高性能,尤其是在有大量服务提供者但只使用其中一部分的情况下。

缓存机制:加载过的服务提供者会被缓存起来,后续再次调用iterator()方法时,会返回与之前相同的实例。

三、源码解析

只截部分源码:

通过调用load进行参数设置,不会真正的加载类

public static <S> ServiceLoader<S> load(Class<S> service) {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return new ServiceLoader<>(Reflection.getCallerClass(), service, cl);
}

private ServiceLoader(Class<?> caller, Class<S> svc, ClassLoader cl) {
    Objects.requireNonNull(svc);

    if (VM.isBooted()) {
        checkCaller(caller, svc);
        if (cl == null) {
            cl = ClassLoader.getSystemClassLoader();
        }
    } else {

        // if we get here then it means that ServiceLoader is being used
        // before the VM initialization has completed. At this point then
        // only code in the java.base should be executing.
        Module callerModule = caller.getModule();
        Module base = Object.class.getModule();
        Module svcModule = svc.getModule();
        if (callerModule != base || svcModule != base) {
            fail(svc, "not accessible to " + callerModule + " during VM init");
        }

        // restricted to boot loader during startup
        cl = null;
    }

    this.service = svc;
    this.serviceName = svc.getName();
    this.layer = null;
    this.loader = cl;
    this.acc = (System.getSecurityManager() != null)
            ? AccessController.getContext()
            : null;
}

调用iterator后进行准备工作,最终实例化LazyClassPathLookupIterator,再调用iterator.hasNext()时,进行真正的加载类

public Iterator<S> iterator() {
    // create lookup iterator if needed
    if (lookupIterator1 == null) {
        // lookme 这里开始加载类
        lookupIterator1 = newLookupIterator();
    }
    return new Iterator<S>() {
    
        // record reload count
        final int expectedReloadCount = ServiceLoader.this.reloadCount;
    
        // index into the cached providers list
        int index;
    
        /**
         * Throws ConcurrentModificationException if the list of cached
         * providers has been cleared by reload.
         */
        private void checkReloadCount() {
            if (ServiceLoader.this.reloadCount != expectedReloadCount)
                throw new ConcurrentModificationException();
        }
    
        @Override
        public boolean hasNext() {
            checkReloadCount();
            if (index < instantiatedProviders.size())
                return true;
            return lookupIterator1.hasNext();
        }
    
        @Override
        public S next() {
            checkReloadCount();
            S next;
            if (index < instantiatedProviders.size()) {
                next = instantiatedProviders.get(index);
            } else {
                next = lookupIterator1.next().get();
                instantiatedProviders.add(next);
            }
            index++;
            return next;
        }
    
    };
}
// 主要看LazyClassPathLookupIterator,它实现了Iterator接口,当调用hasNext时,会触发加载
private Iterator<Provider<S>> newLookupIterator() {
    assert layer == null || loader == null;
    if (layer != null) {
        return new LayerLookupIterator<>();
    } else {
        Iterator<Provider<S>> first = new ModuleServicesLookupIterator<>();
        Iterator<Provider<S>> second = new LazyClassPathLookupIterator<>();
        return new Iterator<Provider<S>>() {
            @Override
            public boolean hasNext() {
                return (first.hasNext() || second.hasNext());
            }
            @Override
            public Provider<S> next() {
                if (first.hasNext()) {
                    return first.next();
                } else if (second.hasNext()) {
                    return second.next();
                } else {
                    throw new NoSuchElementException();
                }
            }
        };
    }
}

@Override
public boolean hasNext() {
    if (acc == null) {
        // lookme
        return hasNextService();
    } else {
        PrivilegedAction<Boolean> action = new PrivilegedAction<>() {
            public Boolean run() { return hasNextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}
private boolean hasNextService() {
    while (nextProvider == null && nextError == null) {
        try {
            // lookme
            Class<?> clazz = nextProviderClass();
            if (clazz == null)
                return false;
    
            if (clazz.getModule().isNamed()) {
                // ignore class if in named module
                continue;
            }
    
            if (service.isAssignableFrom(clazz)) {
                Class<? extends S> type = (Class<? extends S>) clazz;
                Constructor<? extends S> ctor
                    = (Constructor<? extends S>)getConstructor(clazz);
                ProviderImpl<S> p = new ProviderImpl<S>(service, type, ctor, acc);
                nextProvider = (ProviderImpl<T>) p;
            } else {
                fail(service, clazz.getName() + " not a subtype");
            }
        } catch (ServiceConfigurationError e) {
            nextError = e;
        }
    }
    return true;
}
private Class<?> nextProviderClass() {
    if (configs == null) {
        try {
            String fullName = PREFIX + service.getName();
            if (loader == null) {
                configs = ClassLoader.getSystemResources(fullName);
            } else if (loader == ClassLoaders.platformClassLoader()) {
                // The platform classloader doesn't have a class path,
                // but the boot loader might.
                if (BootLoader.hasClassPath()) {
                    configs = BootLoader.findResources(fullName);
                } else {
                    configs = Collections.emptyEnumeration();
                }
            } else {
                configs = loader.getResources(fullName);
            }
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return null;
        }
        pending = parse(configs.nextElement());
    }
    String cn = pending.next();
    try {
        return Class.forName(cn, false, loader);
    } catch (ClassNotFoundException x) {
        fail(service, "Provider " + cn + " not found");
        return null;
    }
}
一个会写python的Java工程师
最后更新于 2024-09-30