2. Dubbo SPI implementation based on SPI idea

dubbo 

SPI interface definition

  How dubbo defines SPI

 

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})

public @interface SPI {

       /**
       * Default extended dotname.
       */

       String value() default ""; //Specify the default extension point

}

In this way, only the interface class marked with the @SPI annotation on the interface will find the extension point implementation

He defines lookup in the following directories

as follows

Let's take the Protocol interface as an example. The interface is annotated with SPI. The default extension point name is dubbo.

@SPI("dubbo")

public interface Protocol{

     int getDefaultPort();

     @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    void destroy();

}

 

Various protocols are built into dubbo, such as: DubboProtocol InjvmProtocolHessianProtocol WebServiceProtocol, etc.

 

Dubbo default rpc module default protocol implements DubboProtocol, key is dubbo

 

Other implemented protocols are similar,

 

Let's talk about the Extensionloader class in detail next.

 

1、ExtensionLoader.getExtensionLoader(Protocol.class)

Each defined spi interface will build an ExtensionLoader instance, stored in

ConcurrentMap<Class<?>,ExtensionLoader<?>> EXTENSION_LOADERS in this map object

 

2. loadExtensionClasses Read the implementation class in the extension point first read the value value of the SPI annotation, and read the files under the path in turn  with the value as the key of the default extension implementation

 

3. loadFile reads the content in the com.alibaba.dubbo.rpc.Protocol file line by line, and the content of each line is stored in the form of key/value

  Determine whether the class implementation (such as: DubboProtocol) is marked with @Adaptive annotation, if marked, cache this class as the configuration class of the Protocol protocol, and read the next line; otherwise, the adaptation class is generated by modifying the bytecode through javasisit , the follow-up introduction about the function of the configuration class

If the class implementation is not marked with @Adaptive, determine whether the implementation class has a constructor whose input parameter is an interface (that is, whether the DubbboProtocol class has a constructor whose input parameter is Protocol), and if it is a wrapper class, it is cached in the Set<Class of this ExtensionLoader In the <?>> collection, this is actually a decoration mode

If it is neither a configuration object nor a wrapped object, it is the specific implementation object of the extension point

Find out if the @Activate annotation is marked on the implementation class, and it is cached in the map of the variable cachedActivates

Cache implementation classes in cachedClasses for easy access when used.

4. Obtain or create a configuration object getAdaptiveExtension If cachedAdaptiveClass has a value, it means that there is one and only one implementation class marked with @Adaptive, instantiate this object and return if cachedAdaptiveClass is empty, create an adaptation class bytecode.

Why create an adaptation class , one interface has multiple implementations, and the same is true for the SPI mechanism. This is a strategy pattern, but which specific strategy should we choose during code execution? Dubbo adopts the unified data mode com.alibaba.dubbo.common.URL (it is the data model defined by dubbo, not the class of jdk), which will be interspersed in the entire execution process of the system. The protocol type field protocol defined in the URL will be Business sets different protocols. The value of url.getProtocol() can be dubbo or webservice, zookeeper or Redis .

The function of the adaptation class is to select the specific extension point implementation from ExtensionLoader.getExtension( extName ) according to the value extName of url.getProtocol().

So you can use javasist to generate the conditions for the adaptation class

There must be at least one method in the interface method marked with @Adaptive annotation

The method parameters annotated with @Adaptive must have URL type parameters or have the getURL() method in the parameters.

The following is the code used by the createAdaptiveExtensionClassCode() method to generate javasist to generate the Protocol adaptation class

 

import com.alibaba.dubbo.common.extension;
public classProtocol$Adpativeimplements com.alibaba.dubbo.rpc.Protocol {  

//The method without @Adaptive throws an exception if it is called
      public void destroy() {

throw new UnsupportedOperationException(
 "methodpublic abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of     i nterfacecom.alibaba.dubbo.rpc.Protocol is not adaptive method!")

}

 

//The export method in the interface is marked with @Adaptive registration
      publiccom.alibaba.dubbo.rpc.Exporter export(
             com.alibaba.dubbo.rpc.Invokerarg0) throws com.alibaba.dubbo.rpc.Invoker{
            if (arg0 == null)
                   throw newIllegalArgumentException("com.alibaba.dubbo.rpc.Invokerargument == null");
            //There must be a URL attribute in the parameter class

if(arg0.getUrl() == null)                    throw newIllegalArgumentException( "com.alibaba.dubbo.rpc.Invokerargument getUrl() == null"); //Get the unified data model URL from the input parameter 

             

com.alibaba.dubbo.common.URL url = arg0.getUrl();
           String extName =(url.getProtocol() == null ? "dubbo" : url.getProtocol());
           //Get the protocol from the unified data model URL , the protocol name is the key of the spi extension point implementation class

if (extName == null) throw new IllegalStateException( "Fail to getextension(com.alibaba.dubbo.rpc.Protocol) name from url("  + url.toString() + ") usekeys([protocol])");

//Using the dubbo service search mechanism to find the specific extension point implementation according to the name

    com.alibaba.dubbo.rpc.Protocol extension                 =(com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)

   .getExtension( extName)

   //The method to adjust the specific extension point

return extension.export(arg0);
 }

 

//The refer method in the interface is marked with @Adaptive to register
 publiccom.alibaba.dubbo.rpc.Invoker refer( Java .lang.Class arg0,
                    com.alibaba.dubbo.common.URLarg1) throws java.lang.Class {
     

//The unified data model URL cannot be empty

if (arg1 == null)
            throw newIllegalArgumentException("url == null");
     

 com.alibaba.dubbo.common.URL url =arg1;

//Get the protocol from the unified data model URL, the protocol name is the key of the spi extension point implementation class

String extName = (url.getProtocol() == null ?"dubbo" : url.getProtocol());
   if (extName == null)
      thrownewIllegalStateException("Failtogetextension(com.alibaba.dubbo.rpc.Protocol)name from url("+ url.toString() + ") use keys([protocol])");


   //Using the dubbo service search mechanism to find the specific extension point implementation according to the name

com.alibaba.dubbo.rpc.Protocol extension =(com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
.getExtension(extName);
    //Adjust the specific extension point Methods

return extension.refer(arg0, arg1);

}

}

 

5. The above java source code is generated by createAdaptiveExtensionClassCode(). To be loaded and executed by the java virtual machine, it must be compiled into bytecode . Dubbo provides two ways to compile the code: 1) Use JDK tool class to compile 2) Use javassit according to Source code generates bytecode.

As shown above: Generate Adaptive code code Use dubbo's spi extension mechanism to obtain the adaptive code generated by the compiler's configuration class compilation

By the way The difference between the @Adaptive annotation on the implementation class and the interface method

 

1) If there is a hit on the interface method, call ExtensionLoader.getAdaptiveExtension() to get the adaptation class, the java source code will be generated through the previous process, and then compiled into a class loaded by the compiler. However, the implementation strategy of the Compiler is also selected through ExtensionLoader.getAdaptiveExtension(). If it is also compiled into a class file by the compiler, wouldn't it be an endless loop?

ExtensionLoader.getAdaptiveExtension(), for the dubbo spi extension mechanism that has the implementation class annotated @Adaptive, it obtains the adaptation class instead of generating the adaptation class java source code through the previous process , but encounters it when reading the extension file The implementation class is annotated with @Adaptive and the class is cached in the ExtensionLoader as an adaptation class, and the call is returned directly.

6. The Wrap class of the extension point on the automatic Wrap

This is an implementation of the decoration mode. There are many such designs in the jdk input and output stream implementation, which is to enhance the extension point function. Here we take for ProtocolFilterWrapper

 

 

As shown in the Protocol inheritance relationship ProtocolFilterWrapper and ProtocolListenerWrapper, these two classes are decoration objects used to enhance the functions implemented by other extension points. The ProtocolFilterWrapper function is mainly to transparently set a series of filter chains for logging, processing timeout, permission control and other functions in the refer reference to the remote service; ProtocolListenerWrapper is called in the provider's exporter, unporter service and consumer's refer service, destroy call When adding listeners, dubbo provides extensions but does not implement which listeners by default.

How does Dubbo automatically wrap decoration objects on extension points? When ExtensionLoader.loadFile loads the extension point configuration file, the constructor that has the interface type parameter for the extension point class is the package transfer object, which is cached in the collection. When calling the createExtension(name) of the ExtensionLoader to create an extension according to the extension point key, First instantiate the implementation of the extension point. When judging that there is this extension, there is a wrapper class cache. If there is, use the packet converter to enhance the function implemented by this extension point. The following figure is the implementation process

7. IOC The well-known IOC is one of the three basic functions of spring . Dubbo's ExtensionLoader internally implements a simple IOC mechanism when loading the extension implementation to inject the parameters that the extension implementation depends on. The public set method in the implementation and the method with one input parameter, try to get the value from the object factory ObjectFactory and inject it into the extension point implementation.

  The above code should not be understood. Let's take a look at how ObjectFactory obtains objects according to type and name. ObjectFactory is also based on dubbo's spi extension mechanism.

Like the Compiler interface, it configures the class annotation @Adaptive, which is typed on the class AdaptiveExtensionFactory and not generated by javassist compilation.

AdaptiveExtensionFactory holds a collection of all ExtensionFactory objects. The default object factory implemented in dubbo is SpiExtensionFactory andSpringExtensionFactory , their search order sorted by TreeMap is obtained from SpiExtensionFactory first, and if it returns empty, it is obtained from SpringExtensionFactory.

The SpiExtensionFactory factory obtains the object to be injected, that is, to obtain the implementation of the dubbo spi extension, so the incoming parameter type must be an interface type and the interface is annotated with @SPI, and the returned object is a configuration class object.

SpringExtensionFactory, Dubbo uses spring's extension mechanism to integrate well with spring. When publishing or referencing a service, the spring container will be added to the SpringExtensionFact ory factory collection. When the SpiExtensionFactory does not obtain the object, it will traverse the spring container in the SpringExtensionFactory to obtain the object to be injected.


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326402597&siteId=291194637
SPI