Unveiling Dubbo SPI: How Extensions Are Loaded, Adapted, and Wrapped
This article continues the deep dive into Dubbo's SPI mechanism, explaining how implementation classes are constructed, how adaptive extensions are generated, how automatic activation works, and why Dubbo wraps objects, providing a comprehensive view of the framework's extensibility features.
Review of the Previous Article
The first part introduced what SPI is, compared its implementations in Java and Spring, and examined the ExtensionLoader source code for loading and classifying implementation classes.
Implementation Class Construction
Before constructing an implementation object, Dubbo must retrieve it using getExtension , which takes a short name (the key in the SPI file) and a wrap flag (default true) indicating whether the returned object should be wrapped.
The retrieval process involves:
Checking the cache for a Holder associated with the (possibly suffixed) name.
If absent, creating a new Holder and storing it in cachedInstances .
Using double‑checked locking inside the Holder to ensure thread‑safe singleton creation.
When the holder is empty, Dubbo calls createExtension to instantiate the object via the default InstantiationStrategy (usually a no‑arg constructor).
Extension Post‑Processor Callbacks
After instantiation, Dubbo invokes ExtensionPostProcessor.postProcessBeforeInitialization (similar to Spring's BeanPostProcessor ) to allow extensions to modify the instance before it is fully initialized.
Dependency Injection
Dubbo then runs injectExtension . Methods eligible for injection must start with set , have a single parameter, be public, and not be annotated with @DisableInject . For each such method, Dubbo determines the property name by stripping the set prefix and lower‑casing the first character, then obtains the required bean from an ExtensionInjector implementation (e.g., AdaptiveExtensionInjector , SpiExtensionInjector , SpringExtensionInjector , ScopeBeanExtensionInjector ).
ExtensionAccessorAware Callback
If the implementation implements ExtensionAccessorAware , Dubbo injects an ExtensionDirector (essentially an ExtensionLoader factory) so the bean can access other extensions.
Post‑Initialization Callback
After dependency injection, ExtensionPostProcessor.postProcessAfterInitialization is called to perform further enhancements.
Automatic Wrapping
If wrap is true, Dubbo creates a static‑proxy‑style wrapper around the target object. The wrapper class is selected via the @Wrapper annotation, instantiated with the original instance as a constructor argument, injected, and then replaces the original instance in the holder, forming a chain of wrappers that ultimately delegate to the real implementation.
Lifecycle Callback
Dubbo checks whether the implementation implements Lifecycle . If so, it calls initialize() after wrapping.
Adaptive Mechanism
Adaptive extensions are selected at runtime based on URL parameters. Dubbo generates a proxy class for the adaptive interface, compiles it (using Javassist or JDK), and creates an instance via reflection. The proxy decides which concrete implementation to delegate to according to the @Adaptive annotation or generated rules.
Automatic Activation
Dubbo uses the @Activate annotation to automatically activate extensions (e.g., Filters) based on URL parameters, group (provider/consumer), and order. Activated extensions form a chain that is applied before the actual RPC call.
The overall construction flow can be visualized as a series of steps: retrieve or create a holder, instantiate the class, run pre‑initialization post‑processors, inject dependencies, apply accessor callbacks, run post‑initialization post‑processors, optionally wrap the instance, and finally invoke lifecycle methods.
Sanyou's Java Diary
Passionate about technology, though not great at solving problems; eager to share, never tire of learning!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.