Introduction of SPI

SPI (Service Provider Interface) is a built-in Service discovery mechanism in JDK. There are a number of frameworks that use it for extended discovery of services. Simply put, it is a mechanism for dynamic replacement discovery. The advantage of using the SPI mechanism is decoupling, separating the assembly control logic of a third-party service module from the caller’s business code.

Java SPI

Let’s take a look at the Java SPI mechanism:

SPI follows the following conventions:

  • 1. After the service provider provides an implementation of the interface, create a file named “fully qualified name of the interface” in the meta-INF /services directory. The file contains the fully qualified name of the implementation class.
  • 2. The JAR package of the interface implementation class is placed in the classpath of the main program;
  • 3. The main program loads the implementation module dynamically through java.util.ServiceLoader. It scans the meta-INF /services directory to find the fully qualified name of the implementation class and loads the class into the JVM.
  • 4. SPI implementation classes must carry a no-argument constructor;

JavaSPI case

1. Create API modules and formulate specifications

public interface HelloSpi {
    String sayHello();
}
Copy the code

2. Create the IMPL module to implement the API interface

Create two classes: man and woman to implement sayHello

public class ManSpi implements HelloSpi { @Override public String sayHello() { return "man say hello"; } } public class WomanSpi implements HelloSpi { @Override public String sayHello() { return "women say hello"; }}Copy the code

Create a meta-INF /services directory under resources and create a file with the same name as the interface classpath

Then configure the full path class name of the implementation class in the file

Create a test module

Rely on apis and IMPL modules

Public class JavaSpiTest {public static void main(String[] args) {ServiceLoader<HelloSpi> serviceLoader=ServiceLoader.load(HelloSpi.class); For (HelloSpi HelloSpi: serviceLoader) { System.out.println(helloSpi.getClass().getName()+":"+helloSpi.sayHello()); }}}Copy the code

test

First, the configuration file configures only ManSpi

Run to see the result

Then change the configuration to WomanSpi

Run to see the result

It is also possible if all are configured

Dubbo SPI

Why did Dubbo write his own SPI

    1. The STANDARD SPI in the JDK instantiates all implementations of extension points at once, which is time-consuming to initialize if there is an extension implementation, but wasteful of resources to load if it is not used
    1. If any extension points fail to load, all extension points are unavailable
    1. Adaptive wrapping of extension points is provided, and injection of other extension points via set is also supported

1. Create API modules and formulate specifications

You need to annotate the interface with SPI annotations

@SPI
public interface HelloService {
    String sayHello();
}
Copy the code

2. Create the IMPL module to implement the API interface

API modules need to be introduced, with man and Woman implementing interfaces respectively

public class ManHello implements HelloService { @Override public String sayHello() { return "dubbo man say hello"; } } public class WomanHello implements HelloService { @Override public String sayHello() { return "dubbo woman say hello"; }}Copy the code

Create a meta-INF. dubbo directory in the Resources directory and generate the file with the interface full path class name

Set key=value in the file. Key is equivalent to the ID and value is equivalent to the implementation. Of course, only value can be configured

Create a test module

Here we use Dubbo’s own loader

Public DubboSpiTest {public static void main(String[] args) {// ExtensionLoader<HelloService> extensionLoader=ExtensionLoader.getExtensionLoader(HelloService.class); / / traverse extension point final Set < String > supportedExtensions = extensionLoader. GetSupportedExtensions (); for (String supportedExtension : supportedExtensions) { final HelloService extension = extensionLoader.getExtension(supportedExtension); System.out.println(extension.sayHello()); }}}Copy the code

Viewing the Result

Dubbo SPI @Adaptive

Adaptive function in Dubbo mainly solves the problem of how to dynamically select specific extension points. GetAdaptiveExtension encapsulates all extension points corresponding to the specified interface uniformly and dynamically selects extension points through URL. (All registration information in Dubbo is handled in the form of urls.)

1. Add an interface to the API

@SPI
public interface HelloService {
    @Adaptive
    String sayHello(URL url);
}

Copy the code

2. Add an implementation to the IMPL

public class ManHello implements HelloService { @Override public String sayHello(URL url) { return "dubbo URL man say hello"; } } public class WomanHello implements HelloService { @Override public String sayHello(URL url) { return "dubbo URL woman say hello"; }}Copy the code

3. Added test method in test module

This is a test so you can fill in any part of the URL,? The following part is HelloService lowercase, followed by the first letter. The = sign is followed by the key in the configuration file that represents the object to be referenced

public class DubboAdaptiveTest {
    public static void main(String[] args) {
        //hello.service-HelloService
        URL url=URL.valueOf("test://localhost/hello?hello.service=man");
        final HelloService adaptiveExtension = ExtensionLoader.getExtensionLoader(HelloService.class).getAdaptiveExtension();
        final String s = adaptiveExtension.sayHello(url);
        System.out.println(s);
    }
}

Copy the code

Configure man and view the result

Relocation woman

What happens if you don’t configure it? Execute:

An error is reported, which can be resolved by setting the default:

The priority of the parameter is higher than the default.