preface
Dubbo is a distributed, high-performance, transparent RPC services framework designed to provide efficient service governance solutions such as automatic service registration, automatic discovery, etc., so I decided to take a look at the source code to see why.
SPI
A simple example:
/ / interface
public interface TestService {
String getName(a);
}
/ / implementation class
public class TestServiceImpl implements TestService {
@Override
public String getName(a) {
return "test"; }}Copy the code
Create a file ++ in the resources/ meta-INF /services directory with the same name as the ++ interface path
File name: xx.xx.xx.testService
The file content is xx.xx.xx.TestServiceImpl
public static void main(String[] args) {
ServiceLoader<DriverService> serviceLoader = ServiceLoader.load(TestService.class);
for(TestService testService: serviceLoader){ System.out.println(testService.getName()); }}Copy the code
Depending on the implementation class configured in the file, the above code outputs test, which has the advantage of dynamically substituting different implementation classes
The core idea is somewhat similar to the policy pattern, which has an implementation method: write an interface and several different implementation classes, put the implementation classes into a HashMap, and then call different implementation classes based on different keys.
While dynamic substitution of implementation classes is possible, JAVA’s built-in SPI has a number of disadvantages:
- Cannot load on demand. Although ServiceLoader does lazy loading, it is basically only possible to fetch it all by traversing it, i.e. the implementation classes of the interface are loaded and instantiated once. If you don’t want to use some implementation class, or if instantiation of some class is time consuming, it gets loaded and instantiated, and that’s a waste.
- The method of obtaining an implementation class is not flexible. The method can only be obtained in the form of Iterator. The corresponding implementation class cannot be obtained according to a parameter.
- It is not safe for multiple concurrent threads to use instances of the ServiceLoader class.
- When an implementation class is not loaded, an exception is thrown for no real reason, and the error is difficult to locate.
Dubbo-SPI
First download dubbo source code: source address. There is a demo project in the source code, all of the following code is written in the Demo project
Dubbo-spi and SPI are the same idea, but the implementation details are different. Take a look at the sample code:
@SPI("test")
public interface SpiTest {
String getName(a);
@Adaptive
int getAge(URL url);
@Adaptive(value = "country")
String getCountry(URL url);
@Adaptive({"province", "city"})
String getAddress(URL url);
}
Copy the code
This is an interface marked with the SPI tag
public class SpiImpl implements SpiTest{
@Override
public String getName(a) {
return "test";
}
@Override
public int getAge(URL url) {
return 123;
}
@Override
public String getCountry(URL url) {
return "GuangZhou";
}
@Override
public String getAddress(URL url) {
return "China"; }}Copy the code
The implementation class
Then create the file under resouces
The file content: test = org. Apache. Dubbo. Demo. The provider. SpiImpl
// Test the code
public static void main(String[] args) {
ExtensionLoader<SpiTest> loader = ExtensionLoader.getExtensionLoader(SpiTest.class);
SpiTest spiTest = loader.getAdaptiveExtension();
URL url = URL.valueOf("? country=test");
System.out.println(spiTest.getCountry(url));
}
Copy the code
Running the code above will bring up: GuangZhou
Let’s first explain the two annotations used above:
- @SPI(“test”)
SPI is a tag, and the test value corresponding to the test = org. Apache. Dubbo. Demo. The provider. SpiImpl, attention to the bold, and said the implementation class name; This value does not enforce the use of the test implementation class, but is a default that is used if no other implementation class is available
- @Adaptive(value = “country”)
Note the parameters to this URL: country URL URL = url.valueof (“? country=test”); Each SPI method must construct a URL parameter, with country representing the parameter in the URL
If you write an implementation class: test1 = org. Apache. Dubbo. Demo. The provider. SpiImpl1
Url url = url.valueof (“? Country =test1”) then the SpiImpl1 implementation method will be executed
Summarize the meaning of these two annotations and parameters based on multiple tests and documentation:
- @SPI
This annotation can be applied to classes, interfaces, and enumerated classes, all on interfaces in the Dubbo framework, indicating that the interface is a Dubbo SPI interface, meaning an adaptive extension point. The internal argument is used to specify the default value, which is the default call when no implementation class is specified.
- @Adaptive
This annotation can be applied to classes, interfaces, enumerated classes, and methods, mostly methods in Dubbo. – For classes: When this interface is applied to a class, it means that the class directly applies to the default implementation. All implementation classes must have only one @Adaptive annotation, otherwise an exception will be thrown. – value parameter: The value parameter is an array. It is used to match the key parsed from the URL. If no match is found, the hump rule is used.
Read the source code according to the use process
Take a look at ExtensionLoader
Several directory addresses:
“META-INF/services/”
“META-INF/dubbo/”
“META-INF/dubbo/internal/”
Represents the static file storage path, you need to put these paths to find dubbo
What’s left is a bunch of caches, so let’s look at the main method
The previous section verifies that the class passed in meets the criteria: the interface type and the @SPI annotation, and then tries to pull the ExtensionLoader out of the cache, or create a new one if it doesn’t
According to the SpiTest SpiTest = loader. GetAdaptiveExtension (); Take a look at the getAdaptiveExtension() method
Synchronized +double-check, first check whether it has been loaded, after the cache double check, not to create.
The most main or createAdaptiveExtensionClassCode this method
This pile of code is bytecode edited into StringBuilder and then compiled by Compiler, which also uses SPI technology. There are two main compilation techniques: JDK and Javassist, Dubbo uses an adaptive piler fixed implementation by default, using JavAssist as the default compiler.
Directly after see createAdaptiveExtensionClassCode compiled code:
The getName method parameter does not have a URL, so it cannot be called
The Adaptive parameters are resolved, and then the getExtension method of the ExtensionLoader is called to retrieve the implementation class.
As you can see, ExtensionLoader uses the template to wrap the original interface and determine the URL parameters, and then calls getExtension to get the actual implementation class based on the parameter values.
Take a look at the getExtension code
Also check the cache and create it if it doesn’t exist
Look at the createExtension method
The first call goes into the getExtensionClasses method, and then into the method at the beginning of injectExtension Reflection Set to inject parameters.
Check the cache and enter loadExtensionClasses if not
If there are any parameters on the SPI, use the first parameter of the array as the default name
Get the file URL from the urls = classLoader.getResources(fileName) method and continue down loadResource
Separate the key and value, use class.forname to load, and then continue down loadClass
Some determination of the class is then put into the cache, at which point all the classes configured in the mate-INF file are loaded into memory.
Note the isWrapperClass(clazz) method
The Wrapper class in the constructor will put the class into a Set, similar to the AOP Wrapper. This is the easy AOP of SPI.
Specific examples:
public class SpiTestImplWrapper implements SpiTest {
private final SpiTest test;
public TestServiceImplWrapper(SpiTest test){
this.test = test;
}
@Override
public String getName(a) {
return "wrapper:"+testService.getName(); }... Other methods}Copy the code
When TestServiceImplWrapper is found, the system wraps the original TestServiceImpl into TestServiceImplWrapper. AOP – like multi-level nesting.
Wrappers are used in many places in Dubbo
Dubbo-spi is a foundation of Dubbo, which can be found everywhere in the source code. Understanding the principle of SPI helps to read the future code.
Dubo-spi Flowchart:
Stage flow chart
Getting an interface instance
A method is called