1
头图

Hello everyone, I'm Xiao Cai.
A man who wants to be about architecture ! If you also want to be the person I want to be, or follow me to be a companion, so that Xiaocai is no longer alone!

This article mainly introduces the SPI mechanism of

If necessary, you can refer to

If it is helpful, don't forget to 161daea7369b2a ❥

has been opened, 161daea7369b3f Vegetable , students who haven't paid attention, remember to pay attention!

In our last article, we talked about the usage of Agent in Java. Many small partners feel that this method is rather biased, and it is not commonly used in normal development (almost useless). In fact, it is not. It is not commonly used because it is linked to the project. The fact that the project is not commonly used does not mean that the method and mechanism are not commonly used. Therefore, many times we can’t sit and watch the sky when we study. We think that we can not learn if we don’t use it in the project.

Jump entry in the last article: Advanced Java usage, write an agent to invade you?

Then in this article, we will continue to talk about another knowledge point in Java, that is, the SPI mechanism. It still feels unfamiliar at first, so don't back down! Looking down, you will find that it is often seen in the usual development!

1. SPI

Our article is by the 161daea7369c1a problem and uses problems to drive learning. Xiaocai throws a few questions first, and the following will explain and expand on these questions.

  • What is SPI?
  • Difference between SPI and API?
  • Do you usually use SPI?

1. What is SPI

SPI is an abbreviation of three words S 061daea7369ca2 P I interface, literally: service providing interface. It is a set of Java to provide a third party to make or extensible interface, it can be used to enable frame expansion and replacement of components. The specific role is to find service implementations for these extended APIs.

JDK and Java SPI is built to provide a service discovery mechanism used to create scalable, alternative application components, is the java modular and critical add-ons of.

Here we mentioned two concepts, namely modularization and plug-in. Modularity is easy to understand. It is to divide a project into multiple modules. There may be interdependence between modules (that is, through maven). Students who use microservice development are no strangers. If they do not use microservice development It doesn't matter, in order to define the control, service, and repository layers in a single project, each field will also be extracted into modules separately, not in the form of directories~

2. Class loading mechanism

We have already mentioned the SPI does not intend to go directly into SPI. Before going into SPI, let's first understand the class loading mechanism in Java. The class loading mechanism may not be noticed in actual development, but it is ubiquitous, and this is also a hot topic in interviews.

In the JVM, the class loader defaults to the principle of parental delegation. The default class loader includes Bootstarp ClassLoader , Extension ClassLoader and System ClassLoader(Application ClassLoader) , of course, there may be custom class loader ~ custom class loader can inherit java.lang.classloader to achieve

The scope of each class loader is as follows:

  • Bootstrap ClassLoader: Responsible for loading the class files in the rt.jar package that comes with the JDK, and is the parent class of all class loading
  • Extension ClassLoader: The extension class library responsible for loading java loads classes from the directory specified by the jre/lib/ect or java.ext.dirs
  • System ClassLoader: responsible for loading class files from the classpath environment variable

The class loading inheritance diagram is as follows:

1) Parent delegation model

is the parental delegation model?

When a class loader receives the task of loading a class, it will first hand it over to its parent loader to complete, level by level, so it will eventually be passed to the Bootstrap ClassLoader for loading, only when the parent loader cannot complete the loading When the task is loaded, it will try to load it by itself

designed this way?

1. Adopting the principle of parental delegation can avoid repeated loading of the same class. Each loader will delegate to its own parent class loader to load when the class loading task is performed. If the parent class cannot be loaded, it will be loaded by itself to avoid repeated loading. situation

2. The security of class loading can be guaranteed. No matter which loader loads the class, it is ultimately delegated to the top-level loader for loading, ensuring that any loader will eventually get the same class object

The loading process is as follows:

doing this?

The child class loader can use the classes that the parent class loader has loaded, but the parent class loader cannot use the classes loaded by the child class loader (similar to inheritance relationship). Java SPI can be mentioned here. Java provides many service provider interfaces (SPIs), which allow third parties to provide implementations for these interfaces, such as SPI services in the database - JDBC. The interfaces of these SPIs are provided by Java core classes , the implementer is indeed a third party, so there will be problems. The provider is loaded by the Bootstrap ClassLoader, and the implementer is loaded by a third-party custom class loader. At this time, the top-level class loading cannot be loaded by the sub-class loader. kind

=

Workaround

To solve this problem, you have to break the principle of parental delegation

Can be loaded using the thread context class loader (ContextClassLoader)

Thread.currentThread().getContextClassLoader() for classes that want to use the parent class loader to load the child class loader.

For example, if we want to load resources, we can use the following methods:

// 使用线程上下文类加载器加载资源
public static void main(String[] args) throws Exception{
    String name = "java/sql/Array.class";
    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(name);
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        System.out.println(url.toString());
    }
}

3、Java SPI

After talking about the class loading mechanism, let's go back to Java SPI. Let's first familiarize ourselves with the use of SPI through examples.

The use process diagram is as follows:

In a more popular understanding, SPI is actually an strategy mode, which is read based on interface programming and combined with configuration files. This also matches our programming style: pluggable~

The usage example is as follows:

project structure :

  • ICustomSvc : Service provides interface (aka SPI)
  • CustomSvcOne/CustomSvcTwo : Implementer (this is simply implemented directly in a project, or it can be implemented by importing a jar package)
  • cbuc.life.spi.service.ICustomSvc : Configuration file

file content :

Then we start CustomTest view the console results

You can see that it is a method that can be loaded into our implementation class, which means that the SPI function has been implemented

1) Implementation principle

In fact, when we use SPI above, we can see a key class that is ServiceLoader , which is located in the java.util package, we directly click into the load() method to see how to call

Click into the load() method and see the following code from our chief

This block code simply declares the use of the thread context loader, we continue to follow up ServiceLoader.load(service, cl)

There is nothing in this block of code. It declares that the ServiceLoader object is returned. What article does this object have? We can look at this class declaration

public final class ServiceLoader<S> implements Iterable<S>{}

It can be seen that this object implements the Iterable interface, indicating that it has an iterative method. It can be guessed that this is to take out all the implementation classes that SPI

The constructor of this class is as follows

The focus is on the reload() method, we continue to follow up

Here, the comments are taken out together. We can see that the method instantiates the lazy search, which illustrates the Iterable interface. We can first click on the iterator() method to see how it is implemented.

It can be seen that there is a key cache, which stores provider . Every time you operate, it will go to the cache to find it. If it exists, it will be returned. Otherwise, LazyIterator be used to find it. We will enter the LazyIterator class to see how to implement it. The class code is too long. We directly intercept the key code. Interested students can view the complete code by themselves:

Seeing the implementation of the code suddenly became clear, we saw the familiar directory name META-INF/services/ , the code will go to the specified directory to obtain file resources, and then load the class by uploading the incoming thread context class loader, so that our The SPI implementation class can be used for the project~ After reading it, I have to sigh wonderful ~

So far, we have dismantled the use and implementation principle of JAVA SPI. After reading it, do you think this skill is not far away from us~!

4. Summary

The Java SPI mechanism is used to better realize the pluggable , so that the assembly of the third-party service module is separated from the caller's business code, that is decoupling, our application can be carried out according to actual business needs. Dynamic plugging and unplugging.

2. Expansion

Spring SPI

Of course, the SPI mechanism is not only implemented in the JDK. The Spring and Dubbo frameworks we use in our daily development have corresponding SPI mechanisms. Many configurations and implementations in Spring Boot have default implementations. If we want to modify some configurations, we only need to write the corresponding configuration in the configuration file, then the project applies the configuration content we defined, and this One way is to use SPI to achieve.

Difference between Java SPI and Spring SPI

  • The loading tool class used by JDK is ServiceLoader , while Spring uses SpringFactoriesLoader
  • The JDK directory naming method is META-INF/services/ provider interface full class name, while Spring uses META-INF/spring-factories

When using Spring Boot, we will write the full class qualified name to the META-INF/spring.factories file for the class that we want to inject into the IOC container. When the Spring Boot program starts, it will SpringFactoriesLoader , and each jar package class-path will be scanned. META-INF/spring.factories configuration file in the directory, then parse the properties file, find the configuration with the specified name and return

Therefore, SPI can be seen everywhere in our actual development, not only Spring, such as JDBC loading database drivers, SLF4J loading log implementations of different providers, and Dubbo using SPI to implement framework extensions, etc.

Don't talk empty-handed, don't be lazy, and be a X as an architecture~ Please follow and be a companion, so that Xiaocai is no longer alone. See you below!

If you work harder today, tomorrow you will be able to say one less thing to ask for help!
, a man who grows stronger with you. 💋
has been opened, 161daea736a4df Vegetable , students who haven't paid attention, remember to pay attention!

写做
624 声望1.7k 粉丝