In the introduction to the Java SPI, SPI is a service discovery mechanism. So why do we need Dubbo SPI? Dubbo SPI has many advantages over Java SPI. Dubbo SPI has many advantages over Java SPI. Dubbo SPI has many advantages.

1. Java SPI instantiates all service extension point implementations at one time. If there are extension point implementation classes, it is time-consuming to initialize them, but it is a waste of resources to find out if they are used after loading them.

2. If the extension fails to be loaded, the name of the failed extension cannot be obtained. For example, if RubyScriptEngine fails to load the RubyScriptEngine class because the dependent jruby.jar does not exist, the cause of the failure is eaten. It does not correspond to Ruby. When users execute Ruby scripts, they will report that Ruby is not supported, not the real reason.

3. Added extension point AOP and IOC support, extension point can inject other extension point instances through setter methods

use

Java SPI :(using code from DriverManager)

ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
    while(driversIterator.hasNext()){
        driversIterator.next();
    }
} catch(Throwable t) {
// Do nothing
}
Copy the code

Java SPI loads extension point implementations from files in the same _CLASspath *: meta-INF /services_ and extension point full path

Mysql Driver jar package files in the meta-inf/services/Java, SQL. The Driver is:

com.mysql.jdbc.Drvier
Copy the code

Dubbo SPI: (this is an excerpt of Dubbo using SPI mechanism to load Protocol extension point code)

ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(Constants.REGISTRY_PROTOCOL);                 //or                 .getAdaptiveExtension()                 .getActivateExtension()
Copy the code

GetAdaptiveExtension () can be used to obtain dynamic extension points after Dubbo SPI obtains ExtensionLoader instances. GetActivateExtension () gets… ? These two features will be explained after the basic features are covered.

Dubbo SPI from the following directory

classpath*:META-INF/dubbo/ META-INF/services/ META-INF/dubbo/internal

Load extension point implementation

Dubbo SPI source

After understanding Dubbo SPI compared to Java SPI what advantages and the use of Dubbo SPI and Java SPI, then through the source code to understand how Dubbo SPI is to achieve these functions. We are here to read the source point is ExtensionLoader. GetExtensionLoader (Protocol. The class)

.getExtension(Constants.REGISTRY_PROTOCOL);

Here at the beginning is through ExtensionLoader getExtensionLoader (clazz) to obtain a ExtensionLoader instance, and then through the instance for the specified name getExtension tiling implementation.

1. ExntesionLoader.getExtensionLoader

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) throw new IllegalArgumentException("Extension type == null"); if (! type.isInterface()) { throw new IllegalArgumentException("Extension type(" + type + ") is not interface!" ); } if (! withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!" ); } ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; }Copy the code

Some checks are done here, but the most important one is new ExtensionLoader(type), EXTENSION_LOADERS.

New ExtensionLoader(type): The constructor here is a private constructor

private ExtensionLoader(Class<? > type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }Copy the code

The main function of this ExtensionLoader is to record which extensionPoint fetch extensionpoint implementation: Class<? > type.

And created a objectFactory: ExtensionFactory member variables (behind the objectFactory is Dubbo SPI implementation IOC basis).

And just to make it clear, ObjectFactory: ExtensionFactory this member variable is through ExtensionLoader getExtensionLoader (ExtensionFactory. Class). GetAdaptiveExtesion () To get the extension point implementation. ObjectFactory is Null because ExtensionFactory explicitly does not need any other injection.

After creating an extensionLoader instance, we can get the extensionpoint implementation of the specified name through getExtension(name).

2. extensionLoader.getExtension(name)

public T getExtension(String name) {
    if (name == null || name.length() == 0)
        throw new IllegalArgumentException("Extension name == null");
    if ("true".equals(name)) {
        return getDefaultExtension();
    }
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<Object>());
        holder = cachedInstances.get(name);
    }
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                instance = createExtension(name);
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}
Copy the code

Reading through the code above, ignoring some of the cache details, you see that createExtension(name) is the key, where the implementation code creates the extension point with the specified name.

3. createExtension(name)

private T createExtension(String name) { Class<? > clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance); Set<Class<? >> wrapperClasses = cachedWrapperClasses; if (wrapperClasses ! = null && wrapperClasses.size() > 0) { for (Class<? > wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t); }}Copy the code

Reading about createExtesion(Name), there are four main concerns

1. Classs clazz = getExtensionClasses().get(name)

2. T instance = Clazz.newInstance

3. injectExtension(instance)

4. WrapperClass. GetConsturcotor (type). NewInstance (instance)

The above method can be achieved basically see the name know meaning.

getExtensionClasses().get(name)

private Map<String, Class<? >> getExtensionClasses() { Map<String, Class<? >> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; }Copy the code

You can see that getExtensionClasses() does some caching as well, but the main implementation point is _loadExtensionClasses()_.

// synchronized in getExtensionClasses private Map<String, Class<? >> loadExtensionClasses() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation ! = null) { String value = defaultAnnotation.value(); if (value ! = null && (value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { throw new IllegalStateException("more than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } if (names.length == 1) cachedDefaultName = names[0]; } } Map<String, Class<? >> extensionClasses = new HashMap<String, Class<? > > (); loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY); loadFile(extensionClasses, DUBBO_DIRECTORY); loadFile(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }Copy the code

You can see that loadExtesionClassess works from classpath* : meta-INF /dubbo/; META-INF/services ; META-INF/dubbo/internal Load the contents of the files in the same full path of the Class type in the meta-INF /dubbo/internal directory.

_name:_ Extension point implementation class full path

LoadFile: Map<String,Class<? LoadFile: Map<String,Class<? The >> extensionClasses Map will also be stored in the Holder as a cache after the result is returned.

private Map<String, Class<? >> getExtensionClasses() { Map<String, Class<? >> classes = cachedClasses.get(); if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; } private final Holder<Map<String, Class<? >>> cachedClasses = new Holder<Map<String, Class<? > > > (); public class Holder<T> { private volatile T value; public void set(T value) { this.value = value; } public T get() { return value; }}Copy the code

The Holder is the wrapper class that uses volatile variables to store data and ensure visibility in a multithreaded environment.

Here we see the main content of getExtensionClasses(), which is to load the extension implementation at the specified extension point into the three directories specified by Dubbo and load the extension implementation Class in.

Insert the extension implementation Class into the JVM

Viewing the contents of the loadFile, you can see that the class object implemented by the extension point is loaded into the JVM using class.forname (line,true,classLoader). (There is another way classLoader.loadClass)

clazz.newInstance

After obtaining the Class object with the specified extension implementation by getExtensionClassess().get(name), you can obtain an instance of the extension implementation by clazz.newinstance reflection.

There is nothing to say here about clazz.newinstance reflection creation extension implementation class instances. The important thing to note is that clazz.newinstance is an instance created using the default no-argument constructor, and an exception will be thrown if there is no no-argument constructor.

InjectExtension(instance)

private T injectExtension(T instance) { try { if (objectFactory ! = null) { for (Method method : instance.getClass().getMethods()) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers())) { Class<? > pt = method.getParameterTypes()[0]; try { String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; Object object = objectFactory.getExtension(pt, property); if (object ! = null) { method.invoke(instance, object); } } catch (Exception e) { logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; }Copy the code

InjectExtension (Instance) Here is the implementation of the IOC functionality added to Dubbo SPI.

InjectExtension The object to be injected is also a member variable with a setxxx method, indicating that it is also injected through the setxxx method reflection. For injection into the instance is through objectFacotry getExtension (pt, property) to obtain.

InjectExtension = injectExtension = injectExtension = injectExtension = injectExtension = injectExtension = injectExtension = injectExtension objectFactory.getExtension(pt,proeprty)

ObjectFactory is the ExtensionFactory instance that was initialized when the ExtensionLoader instance was created:

The adaptive implementation of ExtensionFactory is the AdaptiveExtensionFactory class. The adaptive implementation of ExtensionFactory is the AdaptiveExtensionFactory class. We’ll come back to getAdaptvieExtension () later.)

AdaptiveExtensionFactory

. You can see in AdaptiveExtensionFactory getExtension behavior is also in the traversal ExtensionFactory implementation class, call them the getExtension (type, name) method to get the instance. Here the AdaptiveExtensionFactory just does an aggregation.

In all implementations of ExtensionFactory, as long as SpringExtensionFactory and SpiExtensionFactory are the ones that actually get the instance.

SpringExtensionFactory

As you can see, the SpringExtensionFactory uses the ApplicationContext to get the instance from the Spring IOC container and then completes injectExtension for the Dubbo SPI.

SPIExtensionFactory

In the SPIExtensionFactory is through ExtensionLoader. GetExtensionLoader (), Then use getAdaptiveExtension to get an instance from Dubbo SPI’s own IOC container to do the injection in injectExtension SPI’s own IOC container is the cache of ExtensionLoader, the cache of ExtensionLoader instances, the cache of extensionPoint implementation class instances, etc., to perform functions similar to Spring beanFactory.

wrapperClass.getConsturcotor(type).newInstance(instance)

In wrapperClass. GetConstrutor (type). NewInstance (instance) is completed in Dubbo AOP functionality of SPI, then look at how suitable for implementation.

Start by looking at where cachedWrapperClasses came from, trace the assignment of the cachedWrapperClasses member variable, and finally find its assignment in loadFile. (This is where the configuration of the extension point implementation is read from the file mentioned earlier in getExtensionClasses().)

loadFile

Clazz.getconstructor (type) adds extension point implementation classes to cachedWrapperClasses only if clazz.getconstructor (type) exists. If not, a NoSuchMethodException is thrown, which is the processing logic in the figure below.

So what is an extension implementation Class that has an extension constructor extension Class with extension point Class? The extension implementation Class implements the extension point interface, in which there is a constructor with the extension point interface Class, which is not static Proxy design pattern Class structure.

Now that you know what cachedWrapperClasses are, let’s look at the Aop capabilities of Dubbo Spi

WrapperClasses are handled using a for loop, so if there are multiple proxies, then there are layers of them stacked together to do the work of multiple proxies. What is the order of multiple proxies? Answer: Read the configuration line by line in loadeFile and add it to cachedWrapperClasses, so the order of multiple agents is the order in the extension point configuration file. What if it’s in multiple files? So first of all, it must be in directory order, because that’s how it’s read

META-INF/dubbo/internal -> META-INF/dubbo/ -> META-INF/services

So there is a question: what if instance is the proxy itself, does it also proxy over and over again? Instance is created using the clazz.newinstance method, which is the default constructor. If the extension proxy implementation class itself has a default constructor, it will be instantiated again and again. However, the extension proxy implementation class does not have a default constructor. There is only one belt expand point interface class has a constructor, so if the use of deliberately extensionLoder getExtension (proxyName) to get the agency to expand class implements is throws an exception, you can see in the createExtension is also on the exception handling of.

Dubbo Spi AOP implementation is mainly used in the static Proxy mode, in the implementation of Proxy class, Unlike Spring AOP, which generates a Proxy implementation class dynamically through a series of pointcuts/facets configurations (dynamic meaning that the Proxy implementation is loaded into the JVM at runtime if there is no Proxy implementation class in the programming class structure)

Stage summary

To summarize what roles are involved in Dubbo SPI, here’s a class diagram

ExtensionFactory is responsible for implementing IOC functionality in Dubbo Spi.

Java SPI

Here’s a quick look at the roles that are involved in the Java SPI, as well as the class diagram

In Java SPI, the LazyIterator in serviceloader.iterator () is used to read the configuration file (below meta-INF /serivces) and load and instantiate the corresponding Class.

ServiceLoader.load(servcie,loader)

public static <S> ServiceLoader<S> load(Class<S> service,
                                        ClassLoader loader)
{
    return new ServiceLoader<>(service, loader);
}
Copy the code

Here is simply to create a serviceLoader instance, and the Dubbo SPI ExtensionLoader. GetExtensionLoader same, just simple record to load point to expand the class object. The main work is done in the hasNext/ Next methods of LazyIterator returned by serviceloader.iterator ().

serviceLoader.lterator

public Iterator<S> iterator() { return new Iterator<S>() { Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator(); public boolean hasNext() { if (knownProviders.hasNext()) return true; return lookupIterator.hasNext(); } public S next() { if (knownProviders.hasNext()) return knownProviders.next().getValue(); return lookupIterator.next(); } public void remove() { throw new UnsupportedOperationException(); }}; }Copy the code

Can see the return of the iterator is basic lookupIterator: LazyIterator; KnownProviders: do caching.

Look at hasNext and next functionality in LazyIterator

LazyIterator.hasNext

public boolean hasNext() { if (acc == null) { return hasNextService(); } else { PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() { public Boolean run() { return hasNextService(); }}; return AccessController.doPrivileged(action, acc); } } private boolean hasNextService() { if (nextName ! = null) { return true; } if (configs == null) { try { String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } while ((pending == null) || ! pending.hasNext()) { if (! configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next(); return true; }Copy the code

HasNext actually reads the configuration of the implementation classes in the same meta-INF /services/ file as the extension point full path.

LazyIterator.next

public S next() { if (acc == null) { return nextService(); } else { PrivilegedAction<S> action = new PrivilegedAction<S>() { public S run() { return nextService(); }}; return AccessController.doPrivileged(action, acc); } } private S nextService() { if (! hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<? > c = null; try { c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (! service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); } try { S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen }Copy the code

Class. ForName (cn,false,loader) is also used to load the Class into the Jvm. And the Dubbo SPI is constant. This is followed by C. Newinstance to create an instance of the implementation class through reflection.

By reading the source implementations of ExtensionLoader and ServcieLoader, we find that ExtensionLoader only creates instances of the specified implementation class when it obtains the extension point implementation with the specified name, and does not create instances of other implementation classes. (At the very beginning, I thought Dubbo SPI compared to Java SPI does not load redundant Class objects, but does not create redundant instances.)

Question: Is it necessary to not load extra Class objects into the JVM here?

@Adaptive

What is the @Adaptive annotation in Dubbo Spi used for? In real scenarios, an extended-point interface often has multiple implementations. Because Dubbo is urL-driven, the specific implementation can be dynamically controlled through some parameters in the passed URL at runtime, which is the Adaptive implementation of the extension point of Dubbo to be realized by @Adaptive.

This is not the dynamic proxy implementation that some articles say, but a dynamic selection function implementation, which is a kind of strategy mode. Not exactly.)

About the use of @adaptive

Adaptive If you look at the comments for this annotation, you can see that this annotation is typically used for classes and methods. In all of Dubbo, @Adaptive uses only a few classes: AdaptiveExtensionFactory and AdaptiveCompiler. Everything else is just method.

@adaptive is used on the class, indicating that the extension idea class will carry out the self-defined implementation of Adaptive mechanism in it, which can be regarded as a marking function. This is an implementation of Adaptive mechanism.

A so-called Adaptive mechanism implementation like the AdaptiveExtensionFactory described earlier uses SpringExtensionFactory and SPIExtensionFactory to get the instance to be injected. Adaptive means that beans can be retrieved from the Spring IOC container and the Dubbo IOC container for injection.

(The above are personal understanding, there is a good explanation of understanding please tell me ha, let me also know about ha)

@ the Adaptive use on the way, here we will use on the method of dubbo do first, and then know what it is used for), using extensionLoader. GetAdaptiveExtension () will be dynamically generated a subclass tiling. What are the methods in this dynamically generated extension idea class?

Without @ the method of the Adaptive annotations, throw an UnsupportedOperationException inside the method body.

For a method with @Adaptive annotation, the method will be rewritten in a certain format: it is mainly to get the URL parameter in the method, and then obtain a parameter value through the URL, and then find the instance of the implementation class matching the parameter value through ExtensionLoader, and finally call the corresponding method implementation with this instance.

The extension point Protocol in Dubbo is used as an example

@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();
}
Copy the code

There are two method to mark the @ the Adaptive, so in the use of extensionLoader. GetAdaptiveExtension dynamically generated when a subclass is $the Adaptive Protocol

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!" ); } public int getDefaultPort() { throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!" ); } public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");  org.apache.dubbo.common.URL url = arg0.getUrl(); String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName); return extension.export(arg0); } public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); org.apache.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])"); org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); }}Copy the code

Can see getDefaultPort and destory method are thrown UnsupportOperatorException. Exprot and refer method also override the according to certain rules.

With an overview of what @Adaptive does with the method, let’s look at how this $Adaptive subclass is generated.

The first entry is of course: ExtensionLoader getExtensionLoader (type). GetAdaptiveExtension ()

public T getAdaptiveExtension() {
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        } else {
            throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        }
    }

    return (T) instance;
}
Copy the code

As you can see in getExtension, there is caching and so on, so we’ll skip all of that and go to the implementation _createAdaptiveExtension_.

private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e); }}Copy the code

There is also an injection action for the adaptive dynamically generated subclass instance of getAdptiveExtensionClass().newinstance (). (Similar to getExtension, I won’t go into it, but I’ll go back to how to subclass it.)

So obviously, subclassing this action must be in _getAdaptiveExtensionClass_ () there is complete.

private Class<? > getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass ! = null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); } private Class<? > createAdaptiveExtensionClass() { String code = createAdaptiveExtensionClassCode(); ClassLoader classLoader = findClassLoader(); com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }Copy the code

Why call getExtensionClasses in getAaptiveExtensionClass?

Subclassing action seen in createAdaptiveExtensionClass (), createAdaptiveExtensionClassCode () is responsible for generating a subclass of the original code, then the Compiler to compile loaded into a Class object.

The content of the Compiler is don’t chat for a while, take a look at _createAdaptiveExtensionClassCode_ (generation) what is the original code. (emm, the writing is very long, we’ll sections explain, not all post)

This checks that if @adaptive is not available on all methods, there is no need to subclass; If the generated class name is Protocol$Adaptive. (This is just an example; specific classes are different.)

Here for tiling in the interface no @ the method of the Adaptive marker, is thrown in the generating method of the subclass UnsupportOperationnException.

The following else handles the @adaptive annotation tag:

Here we are looking for a URL type parameter in the method body, if there is a URL type parameter in the method body, check if it is null, and assign the value to the URL variable. This URL is used to get the parameters, and then dynamically get the method that implements the class call.

If you don’t find a parameter of the URL type in the method body, look for a parameter in the method body. The get method returns the URL type.

Method returns a null value from get method if it is found.

The above is the added null code, and the following is the url assignment code. Get URL parameter assignment directly from the method body.

Now that we have URL parameters, which parameter in the URL are we going to get and use to dynamically call the implementation class’s methods?

The value[I] is used to specify which parameter to fetch.

So let me go back and see what the value is.

Here you can see that value defaults to the value given in the @Adaptive annotation

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
  
    String[] value() default {};
}
Copy the code

It can be seen that the value of @adaptive can be empty. If it is empty, it takes a result of the extension point interface simpleName conversion as a parameter.

Like blog.csdn.net/weixin_3396… FruitGranter = FruitGranter; FruitGranter = FruitGranter; FruitGranter = FruitGranter; In the case of a Protocol interface, the URL parameter is Protocol.

So know which parameter in the URL is obtained, is that a section of the URL parameter value of the code:

This is easy to understand, is to determine how to use the URL parameter to obtain, and then generate the corresponding code.

After the code for the response to the url parameter value is generated, what does it do?

Here, after we get the URL parameter value, we give extName the variable.

The variables used in ExtensionLoader. GetExtensionLoader. GetExtension (extName), also is the implementation class for the specified name. After receiving the extension instance of the implementation class, the method tagged @Adaptive is called to perform specific implementation operations.

Therefore, the function of @Adaptive is to dynamically invoke different subclasses of the extension point based on the parameters in the URL.

The URL parameters here are either specified in @Adaptive or transformed by the extension point interface.

The dynamic here is that you can choose different implementations based on the parameters in the URL.

For example, if the URL is _dubbo: / / 192.168.0.101:20880? If protocol=dubbo_, then the implementation selected in the protocol $Adaptive subclass is the method implemented in DubboProtocol.

If it is _dubbo: / / 192.168.0.101:20880? Protocol =http_ is the time to use the HttpProtocol implementation method.

In the end, I have basically understood what function the @Adaptive annotation realizes in the method.