Adaptive is called Adaptive extension point annotation.

An interface often has multiple implementation classes, and Dubbo dynamically controls the selection of implementation classes through certain parameters in the URL, which is the extension point adaptive feature of Dubbo.

Adaptive is generally used to modify classes and interface methods. Class-level modifications such as daptiveExtensionFactory and AdaptiveCompiler are mostly on methods.

Decorator method level

When the extension point method is modified by @adaptive, a dynamic one is automatically generated and compiled when Dubbo initializes the extension point

Take the Protocol interface 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

Both the export and refer methods are modified by @adaptive. When Dubbo initializes the extension point, it will generate a Protocol$Adaptive class, which will implement the two methods. The generated code is as follows:

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

Methods have some abstract generic logic that finds and invokes the real implementation class based on the information obtained by parsing the URL, which is clearly a ** dynamic proxy pattern **.

Modifier class level

Take the AdaptiveCompiler class, which is modified at the class level by @Adaptive as the Compiler extension point’s implementation class.

@Adaptive public class AdaptiveCompiler implements Compiler { private static volatile String DEFAULT_COMPILER; public static void setDefaultCompiler(String compiler) { DEFAULT_COMPILER = compiler; } @Override public Class<? > compile(String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER; // copy reference if (name ! = null && name.length() > 0) { compiler = loader.getExtension(name); } else { compiler = loader.getDefaultExtension(); } return compiler.compile(code, classLoader); }}Copy the code

In class in the engineering of the resource/meta-inf/dubbo/internal path can be found under the extension point configuration file org.apache.dubbo.common.compiler.Com piler

adaptive=org.apache.dubbo.common.compiler.support.AdaptiveCompiler
jdk=org.apache.dubbo.common.compiler.support.JdkCompiler
javassist=org.apache.dubbo.common.compiler.support.JavassistCompiler
Copy the code

In this way, when Dubbo loads the extension point, it finds the AdaptiveComiler implementation class based on the adaptive property, and the Compiler method decides whether to call the default implementation or the specified implementation, which is specified by the @SPI annotation on the extension point interface.

@SPI("javassist") public interface Compiler { /** * Compile java source code. * * @param code Java source code * @param classLoader classloader * @return Compiled class */ Class<? > compile(String code, ClassLoader classLoader); }Copy the code

Compared with method level, class level omits the process of generating dynamic proxy class, and the specific implementation is determined by the specified class. In addition, for the same extension point, Adaptive of class level can only have one.