1. Loading mechanism
Dubbo has good scalability thanks to the use of appropriate design patterns, as well as the loading mechanism. Based on Dubbo SPI loading mechanism, the interface and concrete implementation of the whole framework can be completely decoupled.
1.1 Java SPI and Dubbo SPI
** In most cases, the implementer writes the Interface and implements the Interface. The caller only relies on the Interface invocation and does not have the right to choose a different implementation. In terms of users, apis are used directly by application developers.
** Service Provider Interface (SPI) ** The caller formulates the Interface specification and provides it to the external for implementation. The caller selects the external implementation he/she needs during invocation. In terms of users, SPI is used by framework extenders.
Below is a simple example, the use of Java SPI which need in meta-inf/Services/directory, create an interface to the full path name of the file, such as com. Seewo. Dubbo. Demo. SPI. The printService file, the file content for specific implementation class for the full path name, If there are more than one implementation class, separate it with a newline character.
Meta-inf/services/com. Seewo. Dubbo. Demo. Spi. The printService file contents in order to realize the full path name of a class com. Seewo. Dubbo. Demo. Spi. PrintServiceImpl com.seewo.dubbo.demo.spi.PrintServiceNewImpl public interface PrintService { void printInfo(); } public class PrintServiceNewImpl implements PrintService { @Override public void printInfo() { System.out.println("new hello world"); } } public class PrintServiceImpl implements PrintService { @Override public void printInfo() { System.out.println("hello world"); } } public class jdkDemo { public static void main(String[] args) { ServiceLoader<PrintService> serviceServiceLoader = ServiceLoader.load(PrintService.class); // Get all SPI implementations, loop through printInfo for (PrintService PrintService: ServiceServiceLoader) {// Print "new Hello World "" Hello world" printService.printinfo (); }}}Copy the code
Some of the core ServiceLoader implementations are shown below, where the configuration file is saved, and where the iterator is implemented, using the current thread’s ClassLoader when iterating through the loop.
Public final class ServiceLoader<S> implements Iterable<S> {// Private static final String PREFIX = "META-INF/services/"; Private final Class<S> service; Private final ClassLoader loader is used to locate, load, and instantiate classes implemented by the implementer. Private Final AccessControlContext ACC; Private LinkedHashMap<String, S> providers = new LinkedHashMap<>(); / / lazy to find iterator private Java. Util. ServiceLoader. LazyIterator lookupIterator; Public static <S> ServiceLoader<S> load(Class<S> service) {// Use the current thread ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); Private class LazyIterator implements Iterator<S> {class <S> service; ClassLoader loader; Enumeration<URL> configs = null; String nextName = null; Private Boolean hasNextService() {if (configs == null) {try {String fullName = PREFIX + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { //... } / /... } } private S nextService() { String cn = nextName; nextName = null; Class<? > c = null; Try {// reflection load Class c = class.forname (cn, false, loader); } catch (ClassNotFoundException x) {} try {// instantiate S p = service.cast(c.newinstance ()); Providers. Put (cn, p); return p; } catch (Throwable x) { //.. } / /.. }}}Copy the code
Dubbo, as a highly extensible RPC framework, also relies on Java’s SPI, and Dubbo extends Java’s native SPI mechanism to make it more powerful. The SPI effect can also be achieved using the ExtensionLoader in the Dubbo framework, but by adding an @SPI annotation to the interface class and the corresponding implementation class name “IMPl” corresponding key=value(implementation class full path name) on the configuration file.
Meta-inf/dubbo/com. Seewo. Dubbo. Demo. Spi. The printService file contents as follows impl = com. Seewo. Dubbo. Demo. Spi. PrintServiceImpl @ spi (" impl ") public interface PrintService { void printInfo(); } PrintService printService = ExtensionLoader.getExtensionLoader(PrintService.class).getDefaultExtension(); printService.printInfo();Copy the code
As can be seen from the Java SPI principle above, the Java SPI mechanism has the following disadvantages:
- You can only iterate through all implementations and instantiate them. If you have an extension implementation that takes time to initialize but may not be used later, it wastes resources.
- The Z configuration file simply lists all extension implementations without naming them. It is difficult to refer to them accurately in the program.
- Extensions that depend on other extensions cannot be auto-injected and assembled. Extensions are difficult to integrate with other frameworks, such as relying on a Spring bean inside the extension, which is not supported by the native Java SPI.
Dubbo SPI has the following concepts:
-
Extension point: interface;
-
1. The implementation of an extension;
-
Extension adaptive example: Essentially an Extension proxy that implements the Extension point interface. When an extension point’s interface method is called, the actual parameters determine which extension to use. The Dubbo framework automatically decides which implementation to choose based on the parameters in the interface;
-
@spi: This annotation applies to the interface of an extension point, indicating that the interface is an extension point;
-
Adaptive: @adaptive annotations are used for methods that extend interfaces. Indicates that the method is an adaptive method. When Dubbo generates an Adaptive instance for an extension point, it generates code for the method if it has the @adaptive annotation.
1.2 Extension Point Configuration specifications
Dubbo SPI is similar to Java SPI. You need to place an SPI configuration file in the meta-INF/Dubbo/directory. The file name is the full path name of the interface, and the content of the configuration file is key= the full path name of the extension point implementation class. Newline delimiters are used if there are more than one implementation class. Where key is passed as a parameter in the Dubbo SPI annotation. In addition, Dubbo SPI is compatible with the Java SPI configuration path and content configuration mode. When Dubbo framework is started, META-INF/services, META-INF/dubbo/, META-INF/dubbo/internal/
- SPI configuration file path: meta-INF /services, meta-INF /dubbo/, meta-INF /dubbo/internal/
- SPI profile name: full path class name
- File content format: Key =value. Multiple files are separated by newline characters
1.3 Extension point classification and caching
Dubbo SPI is divided into Class cache and instance cache. These two kinds of caches can be divided into ordinary extension class, Wrapper extension class, Adaptive extension class, etc.
- Class cache: When Dubbo SPI obtains extended classes, it first reads them from the cache. If they do not exist in the cache, it loads the configuration file. The classes loaded from the configuration file are cached in memory without instantiation.
- Instance cache: Dubbo caches not only the Class, but also the instantiated object of the Class. Every time it is fetched, it is read from the cache first. If it is not in the cache, it is reloaded and cached.
Cached classes and object instances can be classified according to their characteristics:
- Common extension classes, the extension class implementation configured in the SPI configuration file;
- The Wrapper class has no concrete implementation, but abstracts the general logic, and requires a concrete implementation of the extension interface to be passed in the constructor.
- Adaptive extension classes. An extension interface may have multiple implementation classes, which implementation class to use is dynamically determined and adjusted at run time based on certain parameters in the passed URL.
- Other caches, such as extended class loader caches, extension caches, etc.
The specific cache implementation in ExtensionLoader is as follows:
Public class ExtensionLoader<T> {private static final ConcurrentMap< class <? >, ExtensionLoader<? >> EXTENSION_LOADERS = new ConcurrentHashMap<>(64); Private static final ConcurrentMap<Class<? >, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64); Private final ConcurrentMap<Class<? >, String> cachedNames = new ConcurrentHashMap<>(); Private final Holder<Map<String, Class<? >>> cachedClasses = new Holder<>(); Private final Map<String, Object> cachedMap = new ConcurrentHashMap<>(); Private Final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>(); Private final Holder<Object> cachedAdaptiveInstance = new Holder<>(); //Wrapper Class cache private Set<Class<? >> cachedWrapperClasses; }Copy the code
1.4 Features of extension points
1.4.1 Automatic packaging
When the ExtensionLoader loads an extension, if it finds that the extension class contains other extension points as arguments to its constructor, the extension class is considered a Wrapper class. Take the ProtocolFilterWrapper class as an example. This class inherits the Protocol interface. The constructor of this class injects a Protocol parameter. A common logical abstraction can be encapsulated or subclassed to make it more implementation-focused.
public class ProtocolFilterWrapper implements Protocol { private final Protocol protocol; public ProtocolFilterWrapper(Protocol protocol) { if (protocol == null) { throw new IllegalArgumentException("protocol == null"); } this.protocol = protocol; }... }Copy the code
1.4.2 Automatic Loading
In addition to passing in additional extension instances in the constructor, setter methods are often used to set property values, and the corresponding implementation class is automatically injected by ExtensionLoader when performing extension point initialization. The definition of Transport extension point is as follows. Two parameters are passed in @Adaptive, which are “server” and “Transport” respectively. When the Transport#bind method is called, it dynamically extracts the value of the “server” parameter from the “URL” parameter and uses the corresponding extension implementation class if it matches. If none is matched, value is extracted with the second argument “transport”, or an exception is thrown if none is matched. That is, multiple parameters are passed in @Adaptive, and the matching of classes will be implemented in turn until the exception is finally thrown.
@SPI("netty")
public interface Transporter {
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException;
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
Copy the code
1.4.3 adaptive
In Dubbo SPI, the @Adaptive annotation is used to dynamically determine which specific implementation class to use from parameters in the URL.
1.4.4 Automatic Activation
Using the @activate annotation, you can mark the extension point as enabled by default. The annotation can also set the extension point to be activated automatically under different conditions by passing in different parameters.
2. Use of extension point annotations
2.1 @ SPI annotation
The @spi annotation has a value attribute, and you can pass in different arguments to set the default implementation class for this interface. In many places, Dubbo uses getExtension(Class Type, String Name) to get a concrete implementation of the extension point interface, and then checks the incoming Class to see if it is an interface and has @spi annotations.
@documented @Retention(retentionPolicy.runtime) @target ({elementtype.type}) public @interface SPI {// Default implementation key name String value() default ""; }Copy the code
2.2 @ the Adaptive annotation
The @Adaptive annotation can be annotated on classes, interfaces, enumerations, and methods. Only AdaptiveExtensionFactory and AdaptiveCompiler are annotated at the class level, while the rest are annotated at the method level. Method-level annotations automatically generate and compile a dynamic Adaptive class on the first getExtension, thus achieving the effect of dynamically implementing the class. When initializing the interface of Adaptive annotation, the key value of the URL passed in will be matched first. If the first key is not matched, the second key will be matched until all the keys are matched. If not, the default value of @SPI annotation will be used to match.
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, String[] value() default {}; elementType.method}) public @interface Adaptive {// Can be set to multiple, will be matched by String[] value() default {}; }Copy the code
2.3 @ Activate annotation
@activate can be tagged on classes, interfaces, enumerations, and methods. It is mainly used in scenarios where multiple extension points need to be activated according to different conditions. For example, multiple filters need to be activated at the same time because each Filter implements a different function.
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, Elementtype.method}) public @interface Activate {// The group in the URL is activated if it matches. String[] value() default {}; String[] value() default {}; Int order() default 0; }Copy the code
3. Working principle of ExtensionLoader
ExtensionLoder is the main implementation class of the whole extension mechanism. In this class, configuration loading, extension class cache, adaptive object generation cache are realized.
3.1 ExtensionLoader create
ExtensionLoader is created through the factory method ExtensionFactory, where the factory design pattern is used and there are multiple ExtensionLoader generation implementations.
@SPI
public interface ExtensionFactory {
<T> T getExtension(Class<T> type, String name);
}
Copy the code
The factory class has multiple concrete implementations, which can be seen as SpringExtensionFactory, AdaptiveExtensionFactory, SpiExtensionFactory three concrete factory implementation classes.
spring=org.apache.dubbo.config.spring.extension.SpringExtensionFactory
adaptive=org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=org.apache.dubbo.common.extension.factory.SpiExtensionFactory
Copy the code
The Dubbo and Spring containers are connected through the SpringExtensionFactory, which implements classes that hold static methods of the Spring context and can store the Spring context in a Set so that repeated context references can be added on multiple calls. SpringExtensionFactory saves the Spring context when static methods are called during ReferenBean and ServiceBean initializations.
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean { @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; SpringExtensionFactory.addApplicationContext(applicationContext); } } public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware { @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; SpringExtensionFactory.addApplicationContext(applicationContext); }} public class SpringExtensionFactory implements ExtensionFactory {// SpringExtensionFactory implements ExtensionFactory Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>(); public static void addApplicationContext(ApplicationContext context) { CONTEXTS.add(context); if (context instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) context).registerShutdownHook(); }}... @override public <T> T getExtension(Class<T> type, String name) { Find for (ApplicationContext Context: CONTEXTS) { T bean = BeanFactoryUtils.getOptionalBean(context, name, type); if (bean ! = null) { return bean; } } return null; }}Copy the code
The implementation of SpiExtensionFactory is relatively simple, using the ExtensionLaoder implementation directly to retrieve the returned object.
public class SpiExtensionFactory implements ExtensionFactory { @Override public <T> T getExtension(Class<T> type, String name) {// Check whether the type is interface, If (type.isInterface() && type.isannotationPresent (spi.class)) {ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); if (! loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } } return null; }}Copy the code
AdaptiveExtensionFactory is the default implementation with the @Adaptive annotation, so it will get all the extension class generation factories and cache them. The cached factory classes are sorted by TreeSet, SPI first and Spring second. When using getExtension, It iterates through all the factories, getting the extension classes from the SPI container first, and then from the Spring container if none is found.
@adaptive Public class AdaptiveExtensionFactory implements ExtensionFactory { Including SpiExtensionFactory/SpringExtensionFactory private final List < ExtensionFactory > factories; Public AdaptiveExtensionFactory() {// The factory list is also implemented via SPI, Here you can get to all the factory implementation class ExtensionLoader < ExtensionFactory > loader. = ExtensionLoader getExtensionLoader (ExtensionFactory. Class); List<ExtensionFactory> list = new ArrayList<ExtensionFactory>(); For (String name: * * * * * * * * * * * * * * * * * * * * * * * * *) loader.getSupportedExtensions()) { list.add(loader.getExtension(name)); } factories = Collections.unmodifiableList(list); } @override public <T> T getExtension(Class<T> type, String name) {for (ExtensionFactory factory: factories) { T extension = factory.getExtension(type, name); if (extension ! = null) { return extension; } } return null; }}Copy the code
3.2 Initialization of ExtensionLoader
The initial configuration of ExtensionLoader specifies several resource file loading strategies, which can be set with ExtensionLoader#setLoadingStrategies. If you want to load classes from other directory files, customize the resource loading policy and set it.
Public interface LoadingStrategy extends priorities {String directory(); / / pretreatment default Boolean preferExtensionClassLoader () {return false. } // excludedPackages() {return null; }} / / custom resource loading strategy public class DubboExternalLoadingStrategy implements LoadingStrategy {@ Override public String Directory () {// Directory for saving configuration files return "meta-INF /dubbo/external/"; } @Override public boolean overridden() { return true; } @override public int getPriority() {return MAX_PRIORITY + 1; }}Copy the code
The class information needs to be loaded before the extension instance can be retrieved. Using the ExtensionLoader#getExtensionClasses method, the class information in the cache can be retrieved if it has already been loaded and placed in the cache. If not, call ExtensionLoader#loadExtensionClasses, read the configuration file path, and load the class information in.
private Map<String, Class<? >> loadExtensionClasses() { .... For (LoadingStrategy strategy: Strategies) {// Read the configuration from the configuration file save path and load loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); . } return extensionClasses; } private void loadDirectory(Map<String, Class<? >> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) { String fileName = dir + type; try { .... / / by getResources () or the getSystemResources configuration file if (urls = = null | |! urls.hasMoreElements()) { if (classLoader ! = null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } } if (urls ! While (urls.hasmoreElements ()) {java.net.URL resourceURL = urls.nextelement (); loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages); } } } catch (hrowable t) { ... }}}Copy the code
After loading the extension point configuration, all extension implementation class implementations are retrieved through reflection and cached. When loading the Class file, the extension point type is determined according to the Class identifier, and then cached according to the type classification.
private void loadClass(Map<String, Class<? >> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name, Throws NoSuchMethodException {// If there is @adaptive on the class, The cache for Adaptive class if (clazz isAnnotationPresent (Adaptive. Class)) {cacheAdaptiveClass (clazz, overridden); } else if (isWrapperClass(clazz)) {cacheWrapperClass(clazz); } else { ... String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, n, overridden); } } } } }Copy the code
3.2 Analysis of ExtensionLoader’s main functions
There are three methods of ExtensionLoader: getExtension, getAdaptiveExtension and getActiveExtension, which are also commonly used.
3.2.1 Principles of getExtension
When the getExtension(String Name) method is called, it checks to see if there is any data in the cachedInstance cache and calls createExtension to start the creation.
Public T getExtension(String name) {final Holder<Object> Holder = getOrCreateHolder(name); Object instance = holder.get(); if (instance == null) { synchronized (holder) { instance = holder.get(); If (instance == null) {// Create instance = createExtension(name); holder.set(instance); } } } return (T) instance; } } private T createExtension(String name) { ... T instance = (T) extension_instance.get (clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } injectExtension(instance);} injectExtension(instance);} injectExtension(instance); Set<Class<? >> wrapperClasses = cachedWrapperClasses; If (CollectionUtils isNotEmpty (wrapperClasses)) {/ / traverse extension point wrapper classes, packaging is used to initialize the Class instance for (Class <? > wrapperClass : WrapperClasses) {// Find a wrapper class whose constructor argument is type, Instance = injectExtension((T) wrapperClass.getConstructor (type).newinstance (instance)); } // initialize the extension instance initExtension(instance); return instance; } catch (Throwable t) { ... }}Copy the code
InjectExtension is a general implementation of The Spring IOC function, which obtains all the defined methods of the class through reflection, traverses the methods that start with the set method, and obtains the parameter types of the set method. Then, it uses ExtensionFactory to find extended class instances of the same type. It is injected with the reflection setting, which is more common in getting Wrapper classes.
private T injectExtension(T instance) { .... Try {for (Method Method: instance.getclass ().getmethods ()) {// Skip if (! isSetter(method)) { continue; } // Get the type of the first argument to the method Class<? > pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) { continue; }... String property = getSetterProperty(method); String property = getSetterProperty(method); / / by ExtensionFactory for instance Object Object. = the objectFactory getExtension (pt, the property); // If you get the extension class implementation, call the set method and inject the instance into it if (object! = null) { method.invoke(instance, object); } } catch (Exception e) { ..... } } } catch (Exception e) { ..... } return instance; }Copy the code
3.2.2 Implementation principle of getAdaptiveExtension
In the getAdaptiveExtension method, an implementation string is automatically generated for the extension point interface. The main logic is as follows: Generate a default implementation for every @Adaptive annotated method in the interface (empty implementation for methods without annotations), and each default implementation extracts the Adaptive parameter value from the URL and dynamically loads the extension point based on that. The framework then uses a different compiler to compile the implementation string into an adaptive class and return it.
This annotation can be passed in as a value parameter, which is an array. Adaptived can pass in multiple keys. When initializing the @Adaptive annotated interface, Adaptived will first match the key value of the incoming URL, the first key fails to match the second key, and so on. Until all keys are matched, if not, the @spi annotation will use the default values specified in the annotation. If the @spi annotation does not have a default value, an exception will be thrown.
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) { ... } } } } return (T) instance; } // Private Class<? > createAdaptiveExtensionClass () {/ / generated class defines a String String code = new AdaptiveClassCodeGenerator (type, cachedDefaultName).generate(); ClassLoader classLoader = findClassLoader(); org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }Copy the code
3.2.3 Implementation principle of getActivateExtension
The getActiveExtension(URL URL, String Key, String Group) method gets all automatic active extension points. The parameters are URL, multiple keys specified in the URL (separated by multiple commas), and group specified in the URL.
支那
支那
public List<T> getActivateExtension(URL url, String[] values, List<T> activateExtensions = new ArrayList<>(); List<T> activateExtensions = new ArrayList<>(); List<String> names = values == null ? new ArrayList<>(0) : asList(values); if (! Contains (REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {// Load getExtensionClasses(); EntrySet () {String name = entry.getKey(); // Map cache for (map.entry <String, Object> Entry: cachedear.entryset ()) {String name = entry.getKey(); Object activate = entry.getValue(); String[] activateGroup, activateValue; if (activate instanceof Activate) { activateGroup = ((Activate) activate).group(); activateValue = ((Activate) activate).value(); } else { continue; } // If (isMatchGroup(group, activateGroup) &&! names.contains(name) && ! names.contains(REMOVE_VALUE_PREFIX + name) && isActive(activateValue, url)) { activateExtensions.add(getExtension(name)); }} / / according to user's URL activateExtensions. Sort PARATOR (ActivateComparator.COM); }... }}Copy the code
4. Extension point dynamic compilation implementation
Dynamic compilation is the basis for the implementation of adaptive features. Dynamically generated adaptive classes are just strings that need to be compiled to be truly converted to Class. The purpose of dynamic compilation is to improve performance. Instead of using reflection to proxy a Class every time, a Class is generated by direct compilation. Dubbo SPI creates new adaptive classes from the original classes through dynamic generation of code and in conjunction with the dynamic compiler. Dubbo code compilers include JDK, Javassist and Adaptive compilers, all of which implement Compiler interface.
The Compiler interface has an @spi annotation, and the default code Compiler implementation class is Javassist. Java dynamically generates classes in many ways. Most of them are generated by bytecode, such as CGLIB/ASM, while Javassist is generated by recompiling string code into a Class.
String getSimpleCodeWithSyntax0(){StringBuilder code = new StringBuilder(); code.append("package org.apache.dubbo.common.compiler.support;" ); code.append("public class HelloServiceImpl_0 implements HelloService {"); code.append(" public String sayHello() { "); code.append(" return \"Hello world! \ "; "); code.append(" }"); return code.toString(); } class public void testCompileJavaClass1() throws Exception {JavassistCompiler Compiler = new JavassistCompiler(); Class<? > clazz = compiler.compile(getSimpleCodeWithSyntax0(), JavassistCompiler.class.getClassLoader()); Object instance = clazz.newInstance(); Method sayHello = instance.getClass().getMethod("sayHello"); sayHello.invoke(instance); }Copy the code
5. To summarize
This article introduces some general information about Dubbo SPI, including the difference of Java SPI, implementation principle, new features of Dubbo SPI, configuration specification and internal cache, etc. The most important annotation is @spi / @adaptive /@Activate. Then combining with core implementation class ExtensionLoader, explained the generative mechanism of it/initialization mechanism/using getExtension/getAdaptiveExtension/getActivateExtension three main methods of implementation, Finally, dynamic compilation is explained.
reference
www.cnblogs.com/jy107600/p/… SPI Principle analysis
www.baidu.com/link?url=rY… Reflection versus dynamic proxy performance