SPI

概述

SPI(Service Provider Interface)

JDK内置的一种 服务提供发现机制

用来 启用框架扩展和替换组件

 

服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类;

当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了;

 

JDK中查找服务的实现的工具类是:java.util.ServiceLoader

ServiceLoader.load(Search.class)在加载某接口时,会去META-INF/services下找接口的全限定名文件,再根据里面的内容加载相应的实现类;

 

如何使用SPI机制

1、定义标准

  如java.sql.Driver

2、不同服务提供方实现

  如MySQL的com.mysql.jdbc.Driver实现,在MySQL的mysql-connector-java-5.1.46.jar中的META-INF/service中有个java.sql.Driver的文件,里面的内容是对应的MySQLDriver实现;

    

3、应用

  ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);

 

广泛应用

jdbc Driver

java中定义了java.sql.Driver,由不同的数据库厂商提供实现;

MySQL的mysql-connector-java-5.1.46.jar中的META-INF/service中有个java.sql.Driver的文件,里面的内容是对应的MySQLDriver实现;

 

springboot中的SPI机制

在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。

从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。

需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中;

...

 

ServiceLoader

概述

A simple service-provider loading facility.

一个简单的 service provider 加载工具

 

A <i>service</i> is a well-known set of interfaces and (usually abstract) classes.
A <i>service provider</i> is a specific implementation of a service.
The classes in a provider typically implement the interfaces and subclass the classes defined in the service itself.
Service providers can be installed in an implementation of the Java platform in the form of extensions, that is, jar files placed into any of the usual extension directories.
Providers can also be made available by adding them to the application's class path or by some other platform-specific means.

service 是一组接口或抽象类

service provider 是一个明确的 service实现

service provider 能够以扩展的形式 被安装在Java平台(将jar文件放置到任何常用的扩展目录中);

service provider 也可以被添加到class path中;

 

For the purpose of loading, a service is represented by a single type, that is, a single interface or abstract class.
(A concrete class can be used, but this is not recommended.)
A provider of a given service contains one or more concrete classes that extend this <i>service type</i> with data and code specific to the provider.

 

service是一个type表示(单个接口或抽象类);

service provider 是给定的service的一个或多个实现;

The <i>provider class</i> is typically not the entire provider itself but rather a proxy which contains enough information to decide whether the provider is able to satisfy a particular request together with code that can create the actual provider on demand.
The details of provider classes tend to be highly service-specific; no single class or interface could possibly unify them, so no such type is defined here.
The only requirement enforced by this facility is that provider classes must have a zero-argument constructor so that they can be instantiated during loading.

该工具强制要求 service providers 提供一个无参构造器

 

A service provider is identified by placing a provider-configuration file in the resource directory META-INF/services.
The file's name is the fully-qualified binary name of the service's type.
The file contains a list of fully-qualified binary names of concrete provider classes, one per line.
Space and tab characters surrounding each name, as well as blank lines, are ignored.
The comment character is '#'; on each line all characters following the first comment character are ignored.
The file must be encoded in UTF-8.

service providers 通过在 META-INF/services 目录下的配置文件 被识别

该配置文件名称 是接口全限定名

该配置文件 存放的是一个 全限定名 的list,每行一个;

空格、tab符 都是不被识别的;

注释符号是 #;

文件的编码格式必须是 UTF-8;

 

If a particular concrete provider class is named in more than one configuration file, or is named in the same configuration file more than once, then the duplicates are ignored.
The configuration file naming a particular provider need not be in the same jar file or other distribution unit as the provider itself.
The provider must be accessible from the same class loader that was initially queried to locate the configuration file; note that this is not necessarily the class loader from which the file was actually loaded.

如果某个service provider在多个配置文件中(或在同一个文件中多个相同名称),重复的将会被忽略;

service provider的配置文件 不需要 与provider 在同一个jar文件中;

 

Providers are located and instantiated lazily, that is, on demand.
A service loader maintains a cache of the providers that have been loaded so far.
Each invocation of the {@link #iterator iterator} method returns an iterator that first yields all of the elements of the cache, in instantiation order, and then lazily locates and instantiates any remaining providers, adding each one to the cache in turn.
The cache can be cleared via the {@link #reload reload} method.

service providers 被定位&初始化 是懒加载的(按需加载);

service loader 维护了一个 被加载的service providers的Cache

每次调用iterator方法都会返回一个iterator,该iterator按实例化顺序生成缓存的所有元素;

Cache可以通过reload方法进行清空

 

Service loaders always execute in the security context of the caller.
Trusted system code should typically invoke the methods in this class, and the methods of the iterators which they return, from within a privileged security context.

service loader总是执行在 调用者 的安全的上下文中;

 

Instances of this class are not safe for use by multiple concurrent threads.

多线程并发使用此实例是线程不安全的;

 

Unless otherwise specified, passing a <tt>null</tt> argument to any method in this class will cause a {@link NullPointerException} to be thrown.

除非明确指定,null参数将会抛出NPE;

 

链路

ServiceLoader.load

// java.util.ServiceLoader.load(java.lang.Class<S>)
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

    // java.util.ServiceLoader.load(java.lang.Class<S>, java.lang.ClassLoader)
    public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
        return new ServiceLoader<>(service, loader);
    }

    // java.util.ServiceLoader.ServiceLoader
    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();
    }

    // java.util.ServiceLoader.reload
    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

    // java.util.ServiceLoader.LazyIterator.LazyIterator
    private LazyIterator(Class<S> service, ClassLoader loader) {
        this.service = service;
        this.loader = loader;
    }

  

serviceLoader.iterator()

// java.util.ServiceLoader.iterator
    public Iterator<S> iterator() {
        return new Iterator<S>() {

            Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();

            public boolean hasNext() {
                if (knownProviders.hasNext())
                    return true;
                return lookupIterator.hasNext();
            }

            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

  

iterator.hasNext()

// java.util.Iterator.hasNext
    public boolean hasNext() {
        if (knownProviders.hasNext())
            return true;
        return lookupIterator.hasNext();
    }

    // java.util.LinkedHashMap.LinkedHashIterator.hasNext
    public final boolean hasNext() {
        return next != null;
    }

    // java.util.ServiceLoader.LazyIterator.hasNext
    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);
        }
    }

    // java.util.ServiceLoader.LazyIterator.hasNextService
    private boolean hasNextService() {
        if (nextName != null) {
            return true;
        }
        if (configs == null) {
            try {
                String fullName = PREFIX + service.getName();           // META-INF/services + 服务全限定名
                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;
    }

  

iterator.next()

// java.util.Iterator.next
    public S next() {
        if (knownProviders.hasNext())
            return knownProviders.next().getValue();
        return lookupIterator.next();
    }

    // java.util.LinkedHashMap.LinkedHashIterator.hasNext
    public final boolean hasNext() {
        return next != null;
    }

    // java.util.ServiceLoader.LazyIterator.next
    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);
        }
    }

    // java.util.ServiceLoader.LazyIterator.nextService
    private S nextService() {
        if (!hasNextService())
            throw new NoSuchElementException();
        String cn = nextName;
        nextName = null;
        Class<?> c = null;
        try {
            c = Class.forName(cn, false, loader);     // Class.forName 反射获取service provider的Class
        } 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());               // 实例化 service provider
            providers.put(cn, p);
            return p;
        } catch (Throwable x) {
            fail(service,
                    "Provider " + cn + " could not be instantiated",
                    x);
        }
        throw new Error();          // This cannot happen
    }

  

posted on 2023-11-28 16:06  anpeiyong  阅读(4)  评论(0编辑  收藏  举报

导航