Do not understand the Java SPI mechanism, how to enter a big factory

introduction

In daily project development, we often use interface-oriented programming ideas for programming in order to improve the scalability of the program. It not only embodies the design principle of closing to modification and opening to expansion, but also realizes the pluggability of the program. So what this article describes today SPIis the embodiment of this programming idea. Today I will talk SPIto you about what the hell it is. By the way, let's take a look at how some common frameworks are used SPIto expand the framework.

Insert picture description here

What is SPI

What we often use is sdkactually a way of implementing an interface and the implementation of the interface in the same jarpackage to complete a business call by calling the interface. But in order to enhance the extended attributes of the program, you can consider using it SPI.

SPI(Service Provider Interface), Which is the service provider interface. It sounds a bit unclear and violent, and I don't know what it means. According to my understanding, it is a service discovery mechanism. The essence is to decouple the interface from the implementation. The server only defines the interface, and the specific implementation is implemented by a third party, which improves the scalability of the program and allows the service provider to program for the interface.

We only need to create a file named after the service interface in the jarpackage META-INF/services/directory at the same time. The file is the name of the specific implementation class that implements the service interface. And when the external program module assembly, which can pass the jarpacket META-INF/services/to find a specific category name in the configuration file, loading and instantiating, completion of loading injection implementation class.

The user provides the rule description, and the actual service provider completes the specific implementation. In fact, this idea is similar to component scanning in spring. The rules are specified first, and the service provider is more standardized to allow the framework to automatically perform service discovery.

Insert picture description here

The key point is here, the knowledge point is here, and the blackboard is knocked on. Since then, we can find that whether it is mentioned in this article SPIor SpringBootthe principle of automatic configuration, it is actually a development idea that convention is greater than configuration . Through pre-agreed content, it can be implemented specifically and dynamically, thereby improving the program's performance. Scalability. So I hope that when you look at a technology, in addition to paying attention to the technical details and vertical understanding, you also need to pay attention to the horizontal technology comparison, so as to find the common ground of these technologies and understand the design ideas behind it. I always think this is very important. , After all, the moves are always changing, but internal strength training is even more important.

SPI source code analysis

1. SPIUse

Java SPIAgreed in the Classpathcase of META-INF/services/creating a service interface to the file named directory, then this is a document which records jarthe fully qualified name of the implementation class of packages. The general process is as follows:

Insert picture description here


2. SPICase analysis

Taking Mysqlthe driver loading as an example, first define the template interface that needs to be extended, that is, the java.sql.Driverinterface. Each database manufacturer can carry out corresponding drive development according to the characteristics of its own database, but must comply with this template interface.

Insert picture description here


In Mysqlthe second party drive package, in which Classpathcase the path META-INF/services/directory, create a service interface is fully consistent with the name of the file saved in this file content fully qualified name of the implementation class template interface.

Insert picture description here


Carry out the concrete class realization in the corresponding catalog, these realization classes all realize the java.sql.Driverinterface.

Insert picture description here


Specific code implementation, ServiceLoadercomplete the instantiation operation of the class by loading the corresponding implementation class. Of course, this ServiceLoadercan also be defined by yourself, frameworks like Dubboand Seataall define their own class loader.

public final class ServiceLoader<S>
    implements Iterable<S>
{
	
	 private static final String PREFIX = "META-INF/services/";
     ...
	 public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }

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

	 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();
    }
    ...
}

Let's analyze the workflow of this service loader together, first by ServiceLoader.load()loading it. First get the current thread-bound ClassLoaderif the current thread is bound ClassLoaderto null, use SystemClassLoaderwere in place, then remove it providercache, create a final LazyIterator. LazyIteratorPart of the source code is as follows:

private class LazyIterator implements Iterator<S>
    {

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;


	...
	
 	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);
            }
        }
	...

	private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                	//key:获取完全限定名
                    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;
        }
        ...


}


key:The specific address of the class is specified by the predetermined directory address and the class name, and the class loader loads the specific implementation class according to this address.

The approximate SPIloading process is as follows:

Insert picture description here


Insert picture description here

Welcome to follow the author’s public account, provide interview guidance, technical sharing and various technical learning materials. Don’t wait, get on the bus.

Insert picture description here