Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

The loading process of Dubbo SPI extension classes

2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/03 Report--

1. JDK SPI

1.1 what is SPI?

SPI (Service Provider Interface), the service provider interface, is a service provision mechanism built into JDK. When writing a program, interface-oriented programming is generally recommended, which has the advantage of reducing the coupling of the program and conducive to the expansion of the program.

SPI also adheres to this concept and provides a unified service interface, and service providers can provide their own specific implementation. The well-known JDBC is based on this mechanism to find the driver provider, whether it is Oracle or MySQL, it is the same when writing code, except that the referenced jar package is different. Later, this concept was also applied to various architectures, such as Dubbo and Eleasticsearch.

1.2 JDK SPI Little Chestnut

The implementation of SPI is to configure the fully qualified name of the interface implementation class in the file, and the service loader reads the configuration file and loads the implementation class.

After understanding the concept, let's look at a specific example.

1) define an interface

Public interface Operation {int operate (int num1, int num2);}

2) write two simple implementations

Public class DivisionOperation implements Operation {public int operate (int num1, int num2) {System.out.println ("run division operation"); return num1/num2;}}

3) add a profile

Add a configuration file under the ClassPath path. The file name is the fully qualified class name of the interface, and the content is the fully qualified class name of the implementation class. Multiple implementation classes are separated by newline characters.

Directory structure

File content

Com.api.impl.DivisionOperationcom.api.impl.PlusOperation

4) Test program

Public class JavaSpiTest {@ Test public void testOperation () throws Exception {ServiceLoader operations = ServiceLoader.load (Operation.class); operations.forEach (item- > System.out.println ("result:" + item.operate (2,2));}}

5) Test results

Source Code Analysis of run division operationresult:1run plus operationresult:41.3 JDK SPI

The example is very simple, implementation, you can boldly guess, look at the name "ServiceLoader" should be based on the type of interface loader plus the specific implementation name in the configuration file to load the implementation.

Then through the analysis of the source code to further understand its implementation principle.

1.3.1 ServiceLoader Class

PREFIX defines the loading path, and the reload method initializes LazyIterator,LazyIterator, which is the core of loading and really implements loading. As you can see from the name, the loaded pattern is a lazy loaded pattern, which is loaded only when the iteration is actually called.

1.3.2 hasNextService method

The hasNextService method in LazyIterator is responsible for loading the configuration file and parsing the specific implementation class name.

1.3.3 nextService method

The nextService method in LazyIterator is responsible for implementing the class with reflection loading.

After reading the source code, I feel that this code has room for optimization, instantiating all the implementations is actually not necessary, on the one hand, it is more time-consuming, and on the other hand, it is a waste of resources. Instead of using Java's native SPI mechanism, Dubbo has enhanced it to better meet the needs.

Dubbo SPI2.1 Dubbo SPI's Little Chestnut

It is an old habit to have a chestnut before disassembling the source code. The example here is a slight modification based on the previous example.

1) define an interface

Modify the interface to add the @ SPI annotation of Dubbo.

SPIpublic interface Operation {int operate (int num1, int num2);}

2) write two simple implementations

Follow the previous two implementation classes.

3) add a profile

The new configuration file is placed in the dubbo directory.

Directory structure

File content

Division=com.api.impl.DivisionOperationplus=com.api.impl.PlusOperation

4) Test program

Public class DubboSpiTest {@ Test public void testOperation () throws Exception {ExtensionLoader loader = ExtensionLoader.getExtensionLoader (Operation.class); Operation division = loader.getExtension ("division"); System.out.println ("result:" + division.operate (1,2));}}

5) Test results

Run division operationresult:02.2 Dubbo SPI source code

The above test example is also very simple. Compared with the native SPI of JDK, the SPI of Dubbo can be obtained according to the configured KV value. Before you disassemble the source code, consider how to implement it.

I might use double-layer Map to implement caching: layer 1 key is the class object of the interface, value is an map; layer 2 key is the extension (key in the configuration file), and value is the class of the implementation class. Implement lazy loading by creating an empty map when the method is running. When you really get it, you first look up the class object of the specific implementation class from the cache, return it directly if you can't find it, and load it according to the configuration file and cache it if you can't find it.

How is Dubbo implemented?

2.2.1 getExtensionLoader method

Let's first disassemble the getExtensionLoader method.

This is a static factory method that requires that the type passed in must be an interface and have a SPI annotation, cached with map, key is the class object of the interface, and value is the ExtensionLoader object.

2.2.2 getExtension method

Let's disassemble the getExtension method of ExtensionLoader.

This code is not complicated either. If the parameter passed in is' true', the default instance of the extension class is returned; otherwise, get the instance from the cache, get it from the cache if there is one, and create a new one if not. Using map as the cache, the holder object is cached, while the holder object stores the extension class. Use the volatile keyword and double checking to deal with the problem of multithreaded creation, which is also a common way to write singleton patterns.

2.2.3 createExtension method

Focus on the analysis of createExtension method.

This code consists of several parts:

Get the corresponding class based on the extension passed in. Get the instance from the cache according to the class, and if not, create the object through reflection and put it into the cache. Dependency injection completes the initialization of the object instance. Create a wrapper object. That is, the object returned here is not necessarily a concrete implementation class, but may be a wrapped object.

The second one has nothing to say, let's focus on the three parts 1, 3 and 4.

1) getExtensionClasses method

The old routine, get from the cache, if not, create and add the cache. What is cached here is the relationship between an extension and class. This extension is the key in the configuration file. The qualified name of the interface is cached before it is created. The paths to load the configuration file are as follows.

2) loadDirectory method

Get the configuration file path, get the classLoader, and use the loadResource method for further processing.

3) loadResource method

LoadResource loads the configuration file and parses the contents of the configuration file. The loadClass method manipulates different caches.

First, determine whether there are Adaptive annotations, and if so, cache them to cacheAdaptiveClass (cache structure is class); then determine whether wrapperclasses is cached in cacheWrapperClass (cache structure is Set); if none of the above, this class is a normal class, and the mapping between class and name is stored in cacheNames (cache structure is Map).

Basically, the getExtensionClasses method has been analyzed, and it can be seen that it is not very complicated.

2.2.4 IOC

1) injectExtension method

This method implements dependency injection, or IOC. First, get the method of the instance through reflection; then iterate to get the setter method; then get the dependent object from the objectFactory; finally, call the setter method through reflection to inject the dependency.

The variable type of objectFactory is AdaptiveExtensionFactory.

2) AdaptiveExtensionFactory

This class contains a list of ExtensionFactory, which is used to store other types of ExtensionFactory. Dubbo provides two kinds of ExtensionFactory, one is SpiExtensionFactory for creating adaptive extensions, and the other is SpringExtesionFactory, which is used to get extensions from Spring's IOC container. The configuration file is one in the dubbo-common module and one in the dubbo-config module.

Configuration file

The Spi mode in SpiExtensionFactory has been parsed previously.

SpringExtesionFactory is to obtain the corresponding instance from ApplicationContext. First look up according to the name, if you can't find it, then look up it according to the type.

The dependency injection part is also disassembled. Take a look at the last part of the code for this disassembly.

2.2.5 AOP

The part that creates the wrapper object, where does the wrapper object come from? Remember the first step of disassembling before, there are several caches in the loadClass method, where wrapperclasses is the class that caches these wrapper.

As you can see from the code, as long as there is only a unique parameter in the constructor, and this parameter is the currently passed interface type, it is wrapper class.

Create a wrapper instance here, first taking instance as a parameter to the constructor, creating a wrapper object through reflection, and then injecting dependencies into the wrapper.

When you see this, some people may wonder: why create a wrapper object? In fact, it is very simple, the system has to do something else before and after the actual call. This is kind of similar to spring's aop.

III. Summary

This paper briefly introduces the SPI of JDK and the SPI usage of Dubbo, and analyzes the SPI source code of JDK and the SPI source code of Dubbo. In the process of disassembly, you can see that the source code of Dubbo is worth reading. In the aspect of implementation, it is considered very carefully, not only the processing of multi-thread and multi-layer cache, but also the process of IOC and AOP. But is Dubbo's SPI that simple? Of course not, this article only unravels the loading process of extension classes, and there is a very complex extension point in Dubbo's SPI-adaptive mechanism.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report