Multiple Instances Make Source Code Development More Complex, Learn How to Correctly Extend SPI Implementations
This article provides a simple summary of the changes related to extending SPI and contributing source code after the multiple-instance transformation of Dubbo3.
This article provides a simple summary of the coding-related changes after the multiple-instance transformation of Dubbo 3.
Hierarchical Model
From only ApplicationModel, new ScopeModel/FrameworkModel/ModuleModel are added to express the hierarchical model of multiple instances. Each ScopeModel instance will create and bind its own important members:
- ExtensionDirector
- BeanFactory
- ServiceRepository
ScopeModel, as the most basic model, can hold and pass in SPI/Bean/URL, etc.
SPI Extension
ExtensionScope
The SPI annotation adds the scope attribute to indicate its belonging scope. The correspondence between ExtensionScope and the hierarchical model:
- FRAMEWORK
- APPLICATION
- MODULE
ExtensionDirector
The new ExtensionDirector is used to implement multi-level SPI management and dependency injection.
The creation process of spi extension through ExtensionDirector is as follows:
- Each SPI can only be created on the ExtensionDirector that matches the Scope, with the aim of sharing instances and correctly injecting dependent objects between levels. The SPI of APPLICATION scope must be created on the ExtensionDirector bound to ApplicationModel, and that of FRAMEWORK scope must be created on the ExtensionDirector bound to FrameworkModel.
- Visibility is related to the scope; here visibility refers to whether dependencies can be directly injected. The SPI of FRAMEWORK scope is visible in FRAMEWORK/APPLICATION/MODULE, while the APPLICATION scope SPI is only visible in APPLICATION/MODULE.
- Invisible SPIs need to be obtained through context, such as passing ScopeModel through URL, which can resolve access issues between FRAMEWORK spi and APPLICATION spi.
The scope of visibility is shown in the diagram below: Upper-level objects can inject SPI/Bean objects of the current and lower levels, while lower-level objects cannot inject SPI/Bean objects of upper levels.
Bean Management
A new ScopeBeanFactory is introduced for internal Bean management, supporting sharing a single instance object across multiple different modules. ScopeBeanFactory also supports scope; the injection rules are the same as for ExtensionDirector. For usage, please refer to: FrameworkStatusReportService, RemoteMetadataServiceImpl, MetadataReportInstance
ServiceRepository
The original ServiceRepository has been split into three classes, corresponding to three different hierarchical models. FrameworkServiceRepository ServiceRepository ModuleServiceRepository
Register the service interface information ProviderModel/ConsumerModel/ServiceDescriptor in ModuleServiceRepository, while keeping a mapping in FrameworkServiceRepository for looking up the corresponding service interface model based on requests.
Coding Changes Summary
1. How to Obtain ApplicationModel and Application Data
Original Method: ApplicationModel provides a series of static methods to obtain shared application instance data
ApplicationModel.getConfigManager()
ApplicationModel.getEnvironment()
ApplicationModel.getServiceRepository()
ApplicationModel.getExecutorRepository()
ApplicationModel.getName()
New Method: Find the ApplicationModel instance first, then use instance methods to obtain data
// Get default instance, compatible with the original single application instance
ApplicationModel.defaultModel().getApplicationEnvironment();
// Get ApplicationModel by Module
moduleModel.getApplicationModel();
// Get ApplicationModel through URL
ScopeModelUtil.getApplicationModel(url.getScopeModel());
// Obtain through Config configuration class
ScopeModelUtil.getApplicationModel(serviceConfig.getScopeModel());
// SPI/Bean can use constructor injection
public ConfigManager(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
}
// SPI/Bean can inject by implementing ScopeModelAware interface
public class DefaultGovernanceRuleRepositoryImpl implements GovernanceRuleRepository, ScopeModelAware {
private ApplicationModel applicationModel;
@Override
public void setApplicationModel(ApplicationModel applicationModel) {
this.applicationModel = applicationModel;
}
// ...
}
// Enumerate all Applications in FrameworkModel
for (ApplicationModel applicationModel : frameworkModel.getApplicationModels()) {
List<RegistryProtocolListener> listeners = applicationModel.getExtensionLoader(RegistryProtocolListener.class)
.getLoadedExtensionInstances();
if (CollectionUtils.isNotEmpty(listeners)) {
for (RegistryProtocolListener listener : listeners) {
listener.onDestroy();
}
}
}
// Enumerate all FrameworkModels
for (FrameworkModel frameworkModel : FrameworkModel.getAllInstances()) {
destroyProtocols(frameworkModel);
}
2. How to Obtain SPI Extension Instances
Original Method: Obtained through static method ExtensionLoader.getExtensionLoader()
ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(name, wrap);
New Method: Obtain through ScopeModel or ExtensionDirector
applicationModel.getExtensionLoader(Cluster.class).getExtension(name, wrap);
3. How to Find Service Models
Original Method: Lookup service models in ServiceRepository by uniqueServiceName.
New Method: Pass ScopeModel/ServiceModel through URL. Please refer to RegistryProtocol.
4. How to Share Bean Instances Across Modules
Original Method: Preserve bean instances using static variables.
New Method: Share instances through BeanFactory.
5. Common Utility Classes and Handling Skills
Obtain the Model of a certain level based on ScopeModel, with compatibility handling; when scopeModel parameter is null, it returns the default instance: ScopeModelUtil.getFrameworkMode(scopeModel) ScopeModelUtil.getApplicationMode(scopeModel) ScopeModelUtil.getModuleMode(scopeModel)
Scenarios Needing Transformation
- ExtensionLoader.getExtensionLoader()
- Application.defaultModel() or other static methods
- Access Application layer objects from the Framework layer, such as processing Application data in Protocol, enumerating all Application data in QOS, please refer to RegistryProtocol.
- Access default instance data in static methods
- Static variable bean instances, cache
- Access data in static methods of SPI interfaces, may need to split into clean SPI and Bean, please refer to FrameworkStatusReportService/FrameworkStatusReporter.
- Some URLs might not have been transformed yet, and need to set ServiceModel/ScopeModel during creation.
Feedback
Was this page helpful?
Yes No
Last modified September 30, 2024: Update & Translate Overview Docs (#3040) (d37ebceaea7)