What is the SPI?

SPI, also known as Service Provider Interface, is a set of APIS provided by Java to be implemented or extended by third parties. It can be used to extend many frameworks, such as Java JDBC, Spring, SpringBoot, Dubbo, etc

Java SPI

The Java SPI is provided by the Java Core library, which defines an interface whose implementation is implemented by a third party. Take Java JDBC Driver as an example, the common relational data have Mysql, Oracle, SQLServer, DB2, etc., these different types of databases use different drivers, that JDK is not possible to implement all vendors’ drivers, can only develop a standard interface, Each database vendor implements its own driver according to the standard, which is the SPI mechanism

Read the egg: sorted out a very comprehensive Java first-line factory Gaegan interview questions analysis collection is now free to share to read this article Java programmer friends, need to get their own

Dynamic Interview Analysis Collection: Java Basics – Intermediate – Advanced Interview +SSM Framework + Distributed + Performance Optimization + Micro Services + Concurrent Programming + Network + Design Patterns + Data Structures and Algorithms +…

Write an example

Java SPI implementation is relatively simple, is a convention, according to its standards to the line, in the implementation of Java SPI needs to meet the following conditions

  • Write an interface and provide the corresponding implementation

  • In the meta-INF /service/ directory, create a file named with the fully qualified name of the interface, such as jdbc.sql.Driver

  • Load it using the serviceloader.load () method

So let’s start coding

Define an interface

2. Implementation interface

3. Add configurations

Create the org.kxg.spi.UserService file in the meta-INF /service/ directory. The content is as follows:

org.kxg.spi.PCUserLoginServiceImpl
org.kxg.spi.AppUserLoginServiceImpl
Copy the code

4. Use ServiceLoader to load

Run the code and the result is as follows:

PC User Login!
APP User Login!
Copy the code

As you can see, the Java SPI is fairly simple to implement. How does it work

How the Java SPI works

1. Start with ServiceLoader

It is primarily the load() method that loads the class

The code is pretty simple, and you end up creating a LazyIterator object. If you look at this and you still don’t understand how the ServiceLoader is loaded, don’t worry

From the above code we can see that ServiceLoader implements the Iterable interface, which is why ServiceLoader can be iterated over

public final class ServiceLoader<S> implements Iterable<S>
Copy the code

Classes that implement the Iterable interface call iterator() methods when iterating through the loop. In other words, the ServiceLoader calls its own iterator() methods when iterating through the loop

As you can see, the main two methods in iterator(), hasNext() and next(), call the hasNext() and next() methods of lookupIterator, thus matching the previous ones. We created a LazyIterator object

Let’s look at the hasNext() and next() methods of lookupIterator

You can see that the main methods are hasNextService() and nextService()

HasNextService () is used to load the contents of files in directories. NextService() is used to instantiate classes

Dubbo SPI

Since Java already provides SPI, why would Dubbo implement SPI himself? Is it just to show off? No, the Java SPI has a downside, as you can see from the above example: Scan the meta-INF /service/ directory and load all the services in the directory, regardless of whether you use them or not. If you encounter a service that takes a long time to load, the overall loading efficiency will be affected

Dubbo builds on the Java SPI, using key-value forms to identify individual services, and adds IOC and AOP support for extension points

First let’s take a look at the configuration of the Dubbo SPI

Protocol, for example

As you can see, Dubbo also names the files with fully qualified names, consistent with the Java SPI. The contents of the files are not consistent with the Java SPI. It is configured as Key=Value, and each implementation specifies a name

registry=com.alibaba.dubbo.registry.integration.RegistryProtocol dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper mock=com.alibaba.dubbo.rpc.support.MockProtocol injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol com.alibaba.dubbo.rpc.protocol.http.HttpProtocol com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol  memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocolCopy the code

Let’s implement a Protocol by customization using an example

In the meta-inf/dubbo/directory new com. Alibaba. Dubbo.. RPC Protocol document, content as follows:

MyProtocol =org.kxg.dubbo.MyProtocol ## This path is the package name + class name of myProtocolCopy the code

Test it by writing a main method

The output

9999
Copy the code

Dubbo’s SPI is the same as Dubbo’s SPI

Dubbo SPI source code analysis

As can be seen from the above example, Dubbo is mainly loaded through the ExtensionLoader, specifically through the getExtensionLoader method to obtain ExtensionLoader, Get the specific object through the ExtensionLoader call getExtension. Let’s look at this step by step

Let’s look at the getExtension method

The general process of SPI above is quite clear, so simply draw a picture to describe it

getExtensionClasses

One important method to get the corresponding classes above is getExtensionClasses, which load configuration files in fixed directories and read the corresponding extension classes

LoadFile code is pretty simple, reads the configuration from the specified directory, because the configuration is the form of key-value pairs, so there will be a corresponding intermediate processing, interested can read the source code

Adaptive extension point

What exactly is Dubbo’s adaptive extension point? We load the extension point according to the configuration, but sometimes we don’t want it to load automatically, we want it to load dynamically with the parameters we pass in. This is the adaptive extension point of Dubbo. Let’s look at an example

Running results:

class com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
Copy the code

If we look at the source code of the AdaptiveCompiler class, we can see that it is marked with the @Adaptive annotation, which is the Adaptive extension point annotation of Dubbo

@ the Adaptive annotations

The @Adaptive annotation can be annotated on classes and methods. The difference between the annotation and the method is that the annotation on the class indicates that the class is an Adaptive extension point class, while the annotation on the method requires dynamic generation of bytecode and then dynamic generation of the class

@adaptive annotations on the class

Or the Complie example above, we follow the source code

If we Debug it, we can see that the corresponding adaptive extension to Complier is adaptive piler

Adaptive annotation in the method

Take Protocol as an example:

We can see that the Protocol interface has two methods annotated with @Adaptive annotation, and the other two methods are not annotated. We mentioned above that the @adaptive annotation will dynamically generate bytecode on the method. Let’s take a look at the source code. We are from getAdaptiveExtensionClass () method to start with

Debug to see what the dynamically generated class file looks like

Let’s format it

As can be seen from the position of the mark in the figure, the dynamically generated class name is Protocol$Adpative. The method marked by @adpative passes in URL parameters and defines extension points according to different URL parameters, which is the adaptive mechanism

@ Activate annotations

The @activate annotation is a bit like Spring’s conditional annotation, which activates the extension point based on the condition. In this case, the @Activate annotation represents the condition to which the extension class is loaded. It has two important attributes

  • Group is filtered by group, and an extension is returned if it is included

  • Value Key filter criteria. If the key in the URL parameter contains this value, the extension is returned

For detailed source code, read the getActivateExtension() method

To summarize

  • SPI, or Service Provider Interface, is a set of apis provided by Java that can be implemented or extended by third parties

  • The Java SPI is provided by the Java Core library, which defines an interface whose implementation is implemented by a third party

  • Java SPI implementation is relatively simple, is a convention, according to its standards to the line, in the implementation of Java SPI needs to meet the following conditions

  • Write an interface and provide the corresponding implementation

  • In the meta-INF /service/ directory, create a file named with the fully qualified name of the interface, such as jdbc.sql.Driver

  • Load it using the serviceloader.load () method

  • The Java SPI works by using the ServiceLoader to load classes in a specified directory

  • Disadvantages of Java SPI: Scan the meta-INF /service/ directory and load all services in that directory, whether you use them or not

  • Dubbo SPI improves on the Java SPI, using key-value forms to identify individual services, and adding IOC and AOP support for extension points

  • Dubbo is mainly used to load extension points through ExtensionLoader

  • Dubbo’s adaptive extension point is where we load the extension point dynamically with the parameters we pass in

  • The @Adaptive annotation can be annotated on classes and methods. The difference between the annotation and the method is that the annotation on the class indicates that the class is an Adaptive extension point class, while the annotation on the method requires dynamic generation of bytecode and then dynamic generation of the class

  • The @Activate annotation is similar to Spring’s conditional annotation, which activates extension points based on conditions