Introduction of SPI implementation principle

Introduction of SPI implementation principle

SericeLoader

from SPI Practical Learning in JAVA One of the most important ways to implement lookup is to:

ServiceLoader shouts = ServiceLoader.load(Handler.class);
Its class structure is as follows:

public final class ServiceLoader<S>
    implements Iterable<S>
{
	//SPI Profile All Paths
    private static final String PREFIX = "META-INF/services/";

    // The class or interface representing the service being loaded
    private final Class<S> service;

    // The class loader used to locate, load, and instantiate providers
    private final ClassLoader loader;

    // The access control context taken when the ServiceLoader is created
    private final AccessControlContext acc;

    // Cached providers, in instantiation order
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

    // The current lazy-lookup iterator
    private LazyIterator lookupIterator;

Load method

A Service Loader is instantiated directly.

return new ServiceLoader<>(service, loader);

In the constructor of ServiceLoader,

    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        //Get the class loader
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }

reload

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

Finding Implementation Classes

The process of finding and creating implementation classes is done in Lazy Iterator. When we call iterator.hasNext and iterator.next methods, we actually call the corresponding methods of Lazy Iterator.

List handlers = Lists.newArrayList(shouts.iterator());
ServiceLoader shouts = ServiceLoader.load(Animal.class);
for (Animal s : shouts) {

        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                //Configuration File Full Path Name of Splice Interface, Load
                    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;
                }
                //Parsing the content in the configuration file
                pending = parse(service, configs.nextElement());
            }
            //Get the class name of an implementation class in the configuration file
            nextName = pending.next();
            return true;
        }

Create examples

When you call the next method, you actually call lookupIterator.nextService. It creates instances of implementation classes by reflection and returns them.

        private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
                //Has been assigned back in the previous step
            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 {
            //Reflect to create instances
                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
        }

next---->nextService----->hasNextService---->class.forName

Keywords: Java

Added by non_zero on Fri, 04 Oct 2019 22:51:25 +0300