Java SPI practical mechanisms Detailed analysis and source code

Background

SPI lift mechanism, many people are not familiar with, it is directly provided by the JDK, called: Service Provider Interface. And rarely encountered in the course of normal use, but if you read some of the source code framework, will find it a bit ubiquitous feeling. For example, we often use spring framework, its spring-web package to use this mechanism.

There log4j logging framework and database-driven framework we can not do without each project is also using the same SPI mechanism.

So it seems, SPI mechanisms are everywhere, so today this article will take you to uncover the mystery of it.

What is the mechanism for SPI

SPI mechanism, the full name of Service Provider Interface, is a set of Java used to be provided by third-party implementation or expansion of API, can be used to enable the framework to extend and replace components, its core classes are java.util.ServiceLoader.

In the large-scale system design, opening and closing the principles and decoupling is essential, the core SPI mechanism is decoupled. By SPI mechanism, the implementation class is hidden behind the interface, service implementation needed to find, SPI provides such a service discovery mechanism.

Effective Java also mentioned SPI is a service interface and service implementation to achieve the separation of decoupling, greatly enhance the procedural mechanisms scalability. The introduction of the service provider is achieved by the introduction of SPI interface, access to a specific category of registration by local discovery, easily removable.

scenes to be used

Background In the beginning, we have found that SPI figure in a different framework. It can be very useful for the "caller according to actual needs, using different strategies to achieve framework of" in.

For example, we use every day is a database-driven, will provide a uniform standard (java.sql.Driver), each logical database service providers to achieve a corresponding database. When the database is used to directly introduce different service implementation to SPI.

Common scenario:

  • Spring framework implemented in a large number, such as the figure above to achieve servlet3.0 Spring ServletContainerInitializer the specification.
  • Database driver loads to achieve different databases, such as the figure above to achieve java.sql.Driver interface.
  • Realization of logging framework log4j.
  • Extended Dubbo implementation framework is implemented.

Use
the following steps to understand the basic norms of the use of SPI:

  • The service provider defines the external interfaces and methods, such as database-driven interface provides a java.sql.Driver.
  • For the interface definition provides an implementation class.
  • Under the project or jar package META-INF / services directory, create a text file: The name is "fully qualified name" interface, content is fully qualified name of the implementation class. The above screenshot may in fact have been found are so unified.
  • The caller introduced service jar package of the project, and place it under classpath.
  • Service the caller through the core API java.util.ServiceLoader to dynamically load the realization, mainly under all jar package under scanning classpath META-INF / services directory, as defined in accordance with the specified format file and where the class is loaded.
  • Since the process can not be used in the SPI mechanism configured passed parameters, thus no need to provide a constructor argument.

Specific examples

No. below to subscribe to the public as an example to demonstrate the use of SPI mechanisms. For convenience, the service users and service providers placed within the same project, normally, a service provider defines the interfaces implemented separately, and then introduced into the project by the service callers form a jar.

First, create a project, define the interfaces Subscribe, and provide a follow method.

public interface Subscribe {
    void follow();
}

Then, the definition of two classes implement: MySubscribe and OtherSubscribe.

public class MySubscribe implements Subscribe {
    @Override
    public void follow() {
        System.out.println("关注了公众号:程序新视界!");
    }
}

public class OtherSubscribe implements Subscribe {
    @Override
    public void follow() {
        System.out.println("关注了其他公众号!");
    }
}

Then, create a META-INF / services directory sequentially in the resources directory, and create a name in the directory: com.secbro2.Subscribe file. Contents of the file:

com.secbro2.impl.MySubscribe
com.secbro2.impl.OtherSubscribe

Finally, write the main method call, the main method is equivalent to the caller SPI mechanisms.

public class Call {

    public static void main(String[] args) {
        ServiceLoader<Subscribe> services = ServiceLoader.load(Subscribe.class);
        for (Subscribe sub : services) {
            sub.follow();
        }
    }
}

Execute the main method to print the following:

关注了公众号:程序新视界!
关注了其他公众号!

Source resolve ServiceLoader

The way we look at the source code information ServiceLoader, first by defining the constants, we can see why you want the file configuration in META-INF / services a.

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

The entire class of all the source code is not posted, briefly explain the basic operation flow of the class.

  • Into the interior of the program by the load ServiceLoader (Class <S> service) method;
  • The method to obtain the above ClassLoader load, and then the inside of this call load (Class <S> service, lassLoader loader) method, an object is created within the ServiceLoader method, and initializes constants.
  • ServiceLoader constructor calls within the reload method to clear the cache, initialization LazyIterator, note here is Lazy, lazy loading it. At this point not to load the contents of the file.
  • When the walker is traversed, only to read the configuration file.

About the core code reading META-INF / services under the following profile:

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

With over-the above code we will find, in fact, ServiceLoader scan configuration files under all the jar packages. It is then obtained by analyzing the fully qualified name, and instantiated by Class.forName while traversing.

summary

After the above explanation and examples, we have learned to use the entire mechanism of SPI, SPI but the mechanism is not a panacea, it also has its own shortcomings. For example, although it uses lazy loading, when you actually use will traverse to load the class, but each class are basically all over again and traverse instantiated, which also caused unnecessary waste. In addition, it is non-thread-safe.

Original link: " the Java source code and explain the actual mechanism of the SPI analysis "

Guess you like

Origin www.cnblogs.com/secbro/p/11634959.html