At the beginning
Dubbo SPI implements spring-like dependency injection and AOP. This section will analyze the implementation principle using dubbo SPI source code
The total process
The calling code
ExtensionLoader<Person> extensionLoader = ExtensionLoader.getExtensionLoader(Person.class); Person person = extensionLoader.getExtension("black"); // BlackPerson URL url = new URL("x", "localhost", 8080); url = url.addParameter("car", "black"); Car car = person.getCar(); String carName = car.getCarName(url); System.out.println(carName); // Proxy logicCopy the code
1. Initialize the extension point loader
Total entrance ExtensionLoader getExtensionLoader, actually nothing, this method is to initialize the dubbo extension point loader
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 an interface!" ); } if (! withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!" ); } 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
2. Obtain the object using the key
Person person = extensionLoader.getExtension("black"); // BlackPerson
Copy the code
This method is important and goes directly to the createExtension method
public T getExtension(String name) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } // Get the default extension class if ("true".equals(name)) {return getDefaultExtension(); } final Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); // If there are two threads fetching the extension point object with the same name, If (instance == null) {synchronized (holder) {// a name corresponds to a lock instance = holder.get(); If (instance == null) {// createExtension point instance object instance = createExtension(name); // Create the extension point object holder.set(instance); } } } return (T) instance; }Copy the code
3. Get the resource file and load it into extensionClasses
In createExtension. GetExtensionClasses (.) loadExtensionClasses (), will conform to the requirements of the format directory configuration files are loaded as extensionClasses map
private Map<String, Class<? > > loadExtensionClasses () {/ / the default cache interface extension class cacheDefaultExtensionName (); Map<String, Class<? >> extensionClasses = new HashMap<>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; }Copy the code
2. If it is a Wrapper class, add it to the global Warpper map in preparation for step 4 AOP
private void loadClass(Map<String, Class<? >> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { if (! type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } / / the current manual specifies the Adaptive interface class if (clazz. IsAnnotationPresent (Adaptive. Class)) {cacheAdaptiveClass (clazz); } else if (isWrapperClass(clazz)) {// is a Wrapper class cacheWrapperClass(clazz); } else { ... }Copy the code
4. Create an extension point object – dependency injection
In step 3, convert the configuration file to clazz and call injectExtension(instance); Starting dependency injection, which is important, does a few things
1. If the current bean relies on another object, such as a Person object that relies on the CAR interface, the set method of Person is iterated and injected using setter methods, such as the car in setCar
2. Call AdaptiveExtensionFactory. GetExtension (” car “)
3. Determine whether the interface is @ SPI annotations, call SpiExtensionFactory. GetExtension (” car “),
4. Initialize the dependent object extension point loaders, call ExtensionLoader. GetExtensionLoader (type)
5. Call 4 step loader loader. GetAdaptiveExtension (), Mr Into interface AdaptiveCompiler class object (agent), which is as for the specific implementation class, here also don’t know, behind by the developer to specify url, you can obtain specific object implementation class
5. Create proxy object -AOP
Here AOP generates proxy objects, cachedWrapperClasses is obtained in step 3, generates a proxy object based on the proxy object key by generating code, generates the proxy object and executes the adaptivecompiler.pile method, Finally, a Car$Adaptive object is generated. The code for the object is shown below, so the final instance returned is actually a carWrapper object
// AOP Set<Class<? >> wrapperClasses = cachedWrapperClasses; if (CollectionUtils.isNotEmpty(wrapperClasses)) { for (Class<? > wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); }}Copy the code
6. Final execution summary
Person.getcar =blacpersion.CarAdaptive- Executes the proxy method above, which, passing black, executes the AOP logic and eventually returns the carWrapper instance, so the carWrapper