You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by me...@apache.org on 2019/09/05 07:13:06 UTC

[dubbo] branch cloud-native updated: Dubbo cloud native (#5008)

This is an automated email from the ASF dual-hosted git repository.

mercyblitz pushed a commit to branch cloud-native
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/cloud-native by this push:
     new 262bc7c  Dubbo cloud native (#5008)
262bc7c is described below

commit 262bc7ce2535cb446096f2375203d9ea7181248b
Author: Mercy Ma <me...@gmail.com>
AuthorDate: Thu Sep 5 15:12:55 2019 +0800

    Dubbo cloud native (#5008)
    
    * Polish apache/dubbo#4542 : [Enhancement] Adapt the Java standard Event/Listener mechanism
    
    * Polish apache/dubbo#4541 : [Feature] Add local File System DynamicConfigurationFactory‘s extension
    
    * Polish apache#4541 : Bugfix
    
    * Polish apache/dubbo#4541 : Optimization
    
    * Polish apache/dubbo#4541 : Add the compatibility for PollingWatchService on the some platforms
    
    * Polish apache/dubbo#4541 : Add delay publish without ThreadPoolExecutor
    
    * Polish apache/dubbo#4541 : Refactor the extension name
    
    * Polish apache/dubbo#4541 : Add remove ops
    
    * Polish apache/dubbo#4541 : Add testable constructor
    
    * Polish apache/dubbo#4541 : Add getConfigGroups method
    
    * Polish apache/dubbo#4610 : [Refactor] Refactor the bootstrap module
    
    * Polish apache/dubbo#4541 : Fix the nulling URL issue
    
    * Polish apache/dubbo#4622 : [Refactor] Refactor ConfigManager
    
    * Polish apache/dubbo#4622 : [Refactor] Refactor ConfigManager
    
    * Polish apache/dubbo#4622 : Support multiple configcenters
    
    * Polish apache/dubbo#4671 : ServiceNameMapping will not map the group, version and protocol
    
    * update referenceCount log (#4683)
    
    Add comments to support multiple shared connections
    
    * Polish /apache/dubbo#4687 : Remove the duplicated test code in dubbo-config-spring (#4688)
    
    * #4685  修改代码if判断false问题 if (hasException == false)修改成if (!hasException) (#4695)
    
    * Fixed Service annotation method parameters are not in effect (#4598)
    
    * keep demo simple, and switch to use zookeeper as registry center (#4705)
    
    * keep demo simple, and switch to use zookeeper as registry center
    
    * remove comment
    
    * @Reference auto-wires the instance of generic interface #4594 (#4677)
    
    * try to shorten maven output to make travis build pass (#4710)
    
    * use CountDownLatch to check zk registry if establish connection (#4589)
    
    * Minor change
    
    * Rename the extension name of WritableMetadataService
    
    * Polish apache/dubbo#4759 : [Refactor] Change the signature of methods of MetadataService #4759
    
    * Merge remote-tracking branch 'upstream/master' into dubbo-cloud-native
    
    # Conflicts:
    #	dubbo-all/pom.xml
    #	dubbo-bom/pom.xml
    #	dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
    #	dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
    #	dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigCenterConfig.java
    #	dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
    #	dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/RegistryConfig.java
    #	dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
    #	dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ReferenceConfigTest.java
    #	dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/DynamicConfiguration.java
    #	dubbo-configcenter/dubbo-configcenter-api/src/test/java/org/apache/dubbo/configcenter/mock/MockDynamicConfiguration.java
    #	dubbo-configcenter/dubbo-configcenter-consul/src/main/java/org/apache/dubbo/configcenter/consul/ConsulDynamicConfiguration.java
    #	dubbo-configcenter/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java
    #	dubbo-configcenter/dubbo-configcenter-nacos/src/main/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfiguration.java
    #	dubbo-configcenter/dubbo-configcenter-nacos/src/test/java/org/apache/dubbo/configcenter/support/nacos/NacosDynamicConfigurationTest.java
    #	dubbo-configcenter/dubbo-configcenter-zookeeper/src/main/java/org/apache/dubbo/configcenter/support/zookeeper/ZookeeperDynamicConfiguration.java
    #	dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/definition/model/MethodDefinition.java
    #	dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/identifier/MetadataIdentifier.java
    #	dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/support/AbstractMetadataReport.java
    #	dubbo-metadata/dubbo-metadata-api/src/test/java/org/apache/dubbo/metadata/report/identifier/MetadataIdentifierTest.java
    #	dubbo-metadata/dubbo-metadata-definition-protobuf/src/main/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilder.java
    #	dubbo-metadata/dubbo-metadata-definition-protobuf/src/test/java/org/apache/dubbo/metadata/definition/protobuf/ProtobufTypeBuilderTest.java
    #	dubbo-metadata/pom.xml
    #	dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/AbstractConfiguratorListener.java
    #	dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistry.java
    #	dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java
    #	dubbo-rpc/dubbo-rpc-xml/src/main/java/org/apache/dubbo/xml/rpc/protocol/xmlrpc/XmlRpcProtocol.java
    
    * Polish apache/dubbo#3984 : Add the implementation of Page<ServiceInstance> getInstances(String serviceName, int offset, int pageSize, boolean healthyOnly)
    
    * Code merge
    
    * Fix the cases
    
    * Merge remote-tracking branch 'upstream/cloud-native' into dubbo-cloud-native
    
    # Conflicts:
    #	dubbo-bootstrap/src/test/java/org/apache/dubbo/bootstrap/DubboServiceProviderBootstrap.java
    #	dubbo-metadata/dubbo-metadata-definition-protobuf/pom.xml
    #	dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/ServiceOrientedRegistryTest.java
    #	dubbo-registry/dubbo-registry-consul/src/main/java/org/apache/dubbo/registry/consul/ConsulServiceDiscoveryFactory.java
    #	dubbo-registry/dubbo-registry-etcd3/src/main/java/org/apache/dubbo/registry/etcd/EtcdServiceDiscovery.java
    
    * Refactor ConfigManager
    
    * Refactor ConfigManager
    
    * Resolve the issues on ConfigManager
    
    * Refactor and add test-cases for ConfigManager
    
    * Polish apache/dubbo#4774 : [Feature] Dubbo Cloud Native - To Support in Spring
    
    * Polish apache/dubbo#4808 : [Feature] Add the registered/unregistered event mechanism ShutdownHook
    
    * Polish apache/dubbo#4807 : [Feature] Add the callback mechanism ShutdownHook #4807
    
    * Polish apache/dubbo#4813 : [Feature] add Prioritized implementation for ServiceInstanceCustomizer
    
    * Polish apache/dubbo#4815 : [Feature] Add the ServiceLoader for Dubbo's services or components
    
    * Polish apache/dubbo#4815 : [Feature] Add the ServiceLoader for Dubbo's services or components
    
    * Polish apache/dubbo#4813 : [Feature] add Prioritized implementation for ServiceInstanceCustomizer
    
    * Polish apache/dubbo#4807 : Add sort implementation
    
    * Refactor
    
    * Refactor
    
    * Polish apache/dubbo#4845 : [Feature] Enhance the Event-Publishing feature to original ServiceDiscovery
    
    * Merge remote-tracking branch 'upstream/cloud-native' into dubbo-cloud-native
    
    # Conflicts:
    #	dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceDiscoveryFactory.java
    #	dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
    
    * Merge remote-tracking branch 'upstream/cloud-native' into dubbo-cloud-native
    
    # Conflicts:
    #	dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/DefaultServiceDiscoveryFactory.java
    #	dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
    
    * Polish apache/dubbo#4854 : [Feature] MetadataService supports the Dubbo protocol under auto-increased port
    
    * Polish apache/dubbo#4857 : [Enhancement] Sync the Metadata storage type into ApplicationConfig
    
    * Polish apache/dubbo#4868 : [Enhancement] Refactor ConfigChangeEvent
    
    * Polish apache/dubbo#4868 : [Enhancement] Refactor ConfigChangeEvent
    
    * Polish apache/dubbo#4873 : [Feature] Add a conditional EventListener into Event Module
    
    * Polish apache/dubbo#4875 : [Feature] Refactor ServiceInstancesChangedListener
    
    * Remove the cycle dependencies
    
    * Remove the cycle dependencies
    
    * Polish apache/dubbo#4903 : [Feature] Set source into the BeanDefinition of Dubbo Config
    
    * Polish apache/dubbo#4902 : [Feature] Dubbo Cloud Native to Spring XML scenario
    
    * Polish apache/dubbo#4713 : Initial the new module and dependencies
    
    * Polish apache/dubbo#4690 : AnnotatedBeanDefinitionRegistryUtils#registerBeans can't remove the duplicated bean definitions
    
    * Polish apache/dubbo#4690 : AnnotatedBeanDefinitionRegistryUtils#registerBeans can't remove the duplicated bean definitions
    
    * Polish apache/dubbo#4690 : AnnotatedBeanDefinitionRegistryUtils#registerBeans can't remove the duplicated bean definitions
    
    * Polish apache/dubbo#4910 : [Feature] To suppoort DubboLifecycleComponentApplicationListener in Spring XML scenario
    
    * Polish apache/dubbo#4713 : Add Service discovery implementation for Eureka #4713
    
    * Polish apache/dubbo#4713 : Add Service registration and discovery implementation for Eureka
    
    * Polish apache/dubbo#4713 : Add Service registration and discovery implementation for Eureka
    
    * Polish apache/dubbo#4920 : [Refactor] Extract the common implementation for URLs' revision
    
    * Refactor
    
    * Polish apache/dubbo#4925 : ServiceDiscovery limits only one ServiceInstancesChangedListener each service
    
    * Polish apache/dubbo#4925 : ServiceDiscovery limits only one ServiceInstancesChangedListener each service
    
    * Remove useless classes
    
    * Bugfix & Refactor ServiceDiscoveryRegistry
    
    * Polish apache/dubbo#4937 : The calculation of Revision should add the parameters of URL
    
    * Polish apache/dubbo#4940 : NacosDynamicConfiguration supports getConfigKeys method
    
    * Polish apache/dubbo#4942 : Dubbo Cloud Native supports multiple protcols
    
    * Polish apache/dubbo#4944 : [Feature] Simplify The metadata of URL for MetadataService
    
    * Polish apache/dubbo#4947 : [Feature] Dubbo Cloud-Native supports the REST call to Non-Dubbo
    
    * Merge remote-tracking branch 'upstream/cloud-native' into dubbo-cloud-native
    
    # Conflicts:
    #	dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
    
    * Refactor
    
    * Update JavaDoc
    
    * Polish apache/dubbo#4905 : Add test cases for Configuration
    
    * Polish apache/dubbo#4905 : Add test cases for AbstractDynamicConfigurationFactory
    
    * Polish apache/dubbo#4905 : Add test cases for ConfigChangeType
    
    * Polish apache/dubbo#4905 : Add test cases for AbstractDynamicConfiguration
    
    * Polish apache/dubbo#4905 : Add test cases for ConfigChangedEvent
    
    * Polish apache/dubbo#4905 : Add test cases for ConfigChangedEvent
    
    * Polish apache/dubbo#4905 : Add test cases for FileSystemDynamicConfiguration
    
    * Polish apache/dubbo#4905 : Add test cases for FileSystemDynamicConfigurationFactory
    
    * Polish apache/dubbo#4905 : Enhancement the robustness for FileSystemDynamicConfiguration
    
    * Polish apache/dubbo#4905 : Add test cases for AdaptiveClassCodeGenerator
    
    * Polish apache/dubbo#4905 : Add test cases for ExtensionLoader
    
    * Polish apache/dubbo#4905 : Add test cases for ThrowableAction, ThrowableConsumer, ThrowableFunction
    
    * Polish apache/dubbo#4905 : Add test cases for Prioritized
    
    * Polish apache/dubbo#4905 : Add test cases for ShutdownHookCallbacks
    
    * Polish apache/dubbo#4905 : Add test cases for DubboServiceLoader
    
    * Polish apache/dubbo#4981 : Add dubbo-common
    
    * Polish apache/dubbo#4907 : Add test cases for DubboServiceDestroyedEvent
    
    * Polish apache/dubbo#4907 : Add test cases for DubboShutdownHookRegisteredEvent
    
    * Polish apache/dubbo#4907 : Add test cases for DubboShutdownHookUnregisteredEvent
    
    * Polish apache/dubbo#4907 : Add test cases for ReferenceConfigDestroyedEvent
    
    * Polish apache/dubbo#4907 : Add test cases for ReferenceConfigInitializedEvent
    
    * Polish apache/dubbo#4907 : Add test cases for ServiceInstancePortCustomizer
    
    * Polish apache/dubbo#4907 : Add test cases for ConfigurableMetadataServiceExporter
    
    * Polish apache/dubbo#4907 : bugfix for @EnableDubbo
    
    * Polish apache/dubbo#4907 : add test cases for @EnableDubboLifecycle
---
 dubbo-all/pom.xml                                  |  46 ++++--
 .../org/apache/dubbo/bootstrap/DubboBootstrap.java |  22 +++
 .../ZookeeperDubboServiceProviderBootstrap.java    |   2 +-
 .../configcenter/AbstractDynamicConfiguration.java |  18 ++-
 .../AbstractDynamicConfigurationFactory.java       |   5 +-
 .../config/configcenter/ConfigChangedEvent.java    |  26 ++--
 .../file/FileSystemDynamicConfiguration.java       |   4 +-
 .../dubbo/common/lang/ShutdownHookCallbacks.java   |   2 +-
 .../org/apache/dubbo/common/utils/UrlUtils.java    |  30 +++-
 .../common/config/InmemoryConfigurationTest.java   |  21 ++-
 .../AbstractDynamicConfigurationFactoryTest.java   |  52 +++++++
 .../AbstractDynamicConfigurationTest.java          | 167 +++++++++++++++++++++
 .../config/configcenter/ConfigChangeTypeTest.java  |  30 ++--
 .../configcenter/ConfigChangedEventTest.java       |  71 +++++++++
 .../FileSystemDynamicConfigurationFactoryTest.java |  40 +++++
 .../file/FileSystemDynamicConfigurationTest.java   |  11 +-
 .../extension/AdaptiveClassCodeGeneratorTest.java  |  48 ++++++
 .../common/extension/ExtensionLoaderTest.java      | 133 +++++++++-------
 .../ThrowableActionTest.java}                      |  18 ++-
 .../ThrowableConsumerTest.java}                    |  18 ++-
 .../ThrowableFunctionTest.java}                    |  18 ++-
 ...sTest.java => DefaultShutdownHookCallback.java} |  15 +-
 .../apache/dubbo/common/lang/PrioritizedTest.java  |   2 +-
 .../common/lang/ShutdownHookCallbacksTest.java     |  32 ++++
 .../DefaultCharSequence.java}                      |  26 +++-
 .../dubbo/common/utils/DubboServiceLoaderTest.java |  32 ++--
 .../META-INF/services/java.lang.CharSequence       |   3 +
 ...g.apache.dubbo.common.lang.ShutdownHookCallback |   1 +
 .../extension/adaptive/HasAdaptiveExt$Adaptive     |  12 ++
 .../dubbo/config/AbstractInterfaceConfig.java      |   7 +-
 .../metadata/ServiceInstancePortCustomizer.java    |   3 +-
 .../event/DubboServiceDestroyedEventTest.java      |  58 +++++++
 .../DubboShutdownHookRegisteredEventTest.java      |  59 ++++++++
 .../DubboShutdownHookUnregisteredEventTest.java    |  60 ++++++++
 .../event/ReferenceConfigDestroyedEventTest.java   |  65 ++++++++
 .../event/ReferenceConfigInitializedEventTest.java |  59 ++++++++
 .../ConfigurableMetadataServiceExporterTest.java   |   2 +-
 .../ServiceInstancePortCustomizerTest.java         |  60 ++++++++
 .../context/annotation/EnableDubboLifecycle.java   |   3 +-
 .../annotation/EnableDubboLifecycleTest.java}      |  45 ++++--
 .../spring/context/annotation/MyLifecycle.java}    |  54 ++++---
 .../org.apache.dubbo.common.context.Lifecycle      |   1 +
 42 files changed, 1192 insertions(+), 189 deletions(-)

diff --git a/dubbo-all/pom.xml b/dubbo-all/pom.xml
index 93b13d0..89de0c6 100644
--- a/dubbo-all/pom.xml
+++ b/dubbo-all/pom.xml
@@ -682,8 +682,15 @@
                                 </includes>
                             </artifactSet>
                             <transformers>
+                                <!-- dubbo-common beginning -->
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler
+                                    </resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
                                     </resource>
                                 </transformer>
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
@@ -692,15 +699,34 @@
                                     </resource>
                                 </transformer>
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.common.infra.InfraAdapter
+                                    </resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter
                                     </resource>
                                 </transformer>
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.common.status.StatusChecker
                                     </resource>
                                 </transformer>
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.common.store.DataStore
+                                    </resource>
+                                </transformer>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>
+                                        META-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool
+                                    </resource>
+                                </transformer>
+                                <!-- dubbo-common end -->
+
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization
                                     </resource>
                                 </transformer>
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
@@ -785,22 +811,10 @@
                                     <resource>META-INF/dubbo/internal/org.apache.dubbo.cache.CacheFactory</resource>
                                 </transformer>
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.common.store.DataStore
-                                    </resource>
-                                </transformer>
-                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter
-                                    </resource>
-                                </transformer>
-                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                     <resource>META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
                                     </resource>
                                 </transformer>
                                 <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-                                    <resource>META-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory
-                                    </resource>
-                                </transformer>
-                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                     <resource>META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
                                     </resource>
                                 </transformer>
diff --git a/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java b/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java
index 900ab1b..21d33e2 100644
--- a/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java
+++ b/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java
@@ -27,6 +27,7 @@ import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.common.utils.UrlUtils;
 import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.config.ConfigCenterConfig;
 import org.apache.dubbo.config.ConsumerConfig;
@@ -602,6 +603,11 @@ public class DubboBootstrap extends GenericEventListener implements Lifecycle {
      */
     @Override
     public DubboBootstrap start() {
+
+        if (!shouldStart()) {
+            return this;
+        }
+
         if (!isInitialized()) {
             initialize();
         }
@@ -632,6 +638,22 @@ public class DubboBootstrap extends GenericEventListener implements Lifecycle {
         return this;
     }
 
+    /**
+     * Should Start current bootstrap
+     *
+     * @return If there is not any service discovery registry in the {@link ConfigManager#getRegistries()}, it will not
+     * start current bootstrap
+     */
+    private boolean shouldStart() {
+        return configManager.getRegistries()
+                .stream()
+                .map(RegistryConfig::getAddress)
+                .map(URL::valueOf)
+                .filter(UrlUtils::isServiceDiscoveryRegistryType)
+                .count() > 0;
+    }
+
+
     private boolean hasExportedServices() {
         return !metadataService.getExportedURLs().isEmpty();
     }
diff --git a/dubbo-bootstrap/src/test/java/org/apache/dubbo/bootstrap/ZookeeperDubboServiceProviderBootstrap.java b/dubbo-bootstrap/src/test/java/org/apache/dubbo/bootstrap/ZookeeperDubboServiceProviderBootstrap.java
index 87d548d..e190b2e 100644
--- a/dubbo-bootstrap/src/test/java/org/apache/dubbo/bootstrap/ZookeeperDubboServiceProviderBootstrap.java
+++ b/dubbo-bootstrap/src/test/java/org/apache/dubbo/bootstrap/ZookeeperDubboServiceProviderBootstrap.java
@@ -29,7 +29,7 @@ public class ZookeeperDubboServiceProviderBootstrap {
                 .application("zookeeper-dubbo-provider")
                 .registry(builder -> builder.address("zookeeper://127.0.0.1:2181?registry-type=service"))
                 .protocol("dubbo", builder -> builder.port(-1).name("dubbo"))
-                .protocol("rest", builder -> builder.port(8082).name("rest"))
+                .protocol("rest", builder -> builder.port(8081).name("rest"))
                 .service("echo", builder -> builder.interfaceClass(EchoService.class).ref(new EchoServiceImpl()).protocolIds("dubbo"))
                 .service("user", builder -> builder.interfaceClass(UserService.class).ref(new UserServiceImpl()).protocolIds("rest"))
                 .start()
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java
index e692733..40d10be 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfiguration.java
@@ -189,11 +189,11 @@ public abstract class AbstractDynamicConfiguration implements DynamicConfigurati
     }
 
     protected static int getThreadPoolSize(URL url) {
-        return url.getParameter(THREAD_POOL_SIZE_PARAM_NAME, DEFAULT_THREAD_POOL_SIZE);
+        return getParameter(url, THREAD_POOL_SIZE_PARAM_NAME, DEFAULT_THREAD_POOL_SIZE);
     }
 
     protected static long getThreadPoolKeepAliveTime(URL url) {
-        return url.getParameter(THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME, DEFAULT_THREAD_POOL_KEEP_ALIVE_TIME);
+        return getParameter(url, THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME, DEFAULT_THREAD_POOL_KEEP_ALIVE_TIME);
     }
 
     protected static String getParameter(URL url, String name, String defaultValue) {
@@ -202,4 +202,18 @@ public abstract class AbstractDynamicConfiguration implements DynamicConfigurati
         }
         return defaultValue;
     }
+
+    protected static int getParameter(URL url, String name, int defaultValue) {
+        if (url != null) {
+            return url.getParameter(name, defaultValue);
+        }
+        return defaultValue;
+    }
+
+    protected static long getParameter(URL url, String name, long defaultValue) {
+        if (url != null) {
+            return url.getParameter(name, defaultValue);
+        }
+        return defaultValue;
+    }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationFactory.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationFactory.java
index 15b9145..e4f6d53 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationFactory.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationFactory.java
@@ -24,14 +24,17 @@ import java.util.concurrent.ConcurrentHashMap;
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY;
 
 /**
+ * Abstract {@link DynamicConfigurationFactory} implementation with cache ability
  *
+ * @see DynamicConfigurationFactory
+ * @since 2.7.4
  */
 public abstract class AbstractDynamicConfigurationFactory implements DynamicConfigurationFactory {
 
     private volatile Map<String, DynamicConfiguration> dynamicConfigurations = new ConcurrentHashMap<>();
 
     @Override
-    public DynamicConfiguration getDynamicConfiguration(URL url) {
+    public final DynamicConfiguration getDynamicConfiguration(URL url) {
         String key = url == null ? DEFAULT_KEY : url.getAddress();
         return dynamicConfigurations.computeIfAbsent(key, k -> createDynamicConfiguration(url));
     }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java
index 8ea32e2..8195558 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEvent.java
@@ -39,7 +39,7 @@ public class ConfigChangedEvent extends EventObject {
     }
 
     public ConfigChangedEvent(String key, String group, String content, ConfigChangeType changeType) {
-        super(key + "=" + content);
+        super(key + "," + group);
         this.key = key;
         this.group = group;
         this.content = content;
@@ -63,28 +63,28 @@ public class ConfigChangedEvent extends EventObject {
     }
 
     @Override
+    public String toString() {
+        return "ConfigChangedEvent{" +
+                "key='" + key + '\'' +
+                ", group='" + group + '\'' +
+                ", content='" + content + '\'' +
+                ", changeType=" + changeType +
+                "} " + super.toString();
+    }
+
+    @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (!(o instanceof ConfigChangedEvent)) return false;
         ConfigChangedEvent that = (ConfigChangedEvent) o;
         return Objects.equals(getKey(), that.getKey()) &&
-                Objects.equals(group, that.group) &&
+                Objects.equals(getGroup(), that.getGroup()) &&
                 Objects.equals(getContent(), that.getContent()) &&
                 getChangeType() == that.getChangeType();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(getKey(), group, getContent(), getChangeType());
-    }
-
-    @Override
-    public String toString() {
-        return "ConfigChangedEvent{" +
-                "key='" + key + '\'' +
-                ", group='" + group + '\'' +
-                ", content='" + content + '\'' +
-                ", changeType=" + changeType +
-                "} " + super.toString();
+        return Objects.hash(getKey(), getGroup(), getContent(), getChangeType());
     }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfiguration.java b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfiguration.java
index bfd7062..284da64 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfiguration.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfiguration.java
@@ -582,13 +582,13 @@ public class FileSystemDynamicConfiguration extends AbstractDynamicConfiguration
     }
 
     protected static File initDirectory(URL url) {
-        String directoryPath = url.getParameter(CONFIG_CENTER_DIR_PARAM_NAME, url.getPath());
+        String directoryPath = getParameter(url, CONFIG_CENTER_DIR_PARAM_NAME, url == null ? null : url.getPath());
         File rootDirectory = null;
         if (!StringUtils.isBlank(directoryPath)) {
             rootDirectory = new File("/" + directoryPath);
         }
 
-        if (!rootDirectory.exists()) { // If the directory does not exist
+        if (directoryPath == null || !rootDirectory.exists()) { // If the directory does not exist
             rootDirectory = new File(DEFAULT_CONFIG_CENTER_DIR_PATH);
         }
 
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallbacks.java b/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallbacks.java
index 70fb774..7aabb77 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallbacks.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallbacks.java
@@ -35,7 +35,7 @@ public class ShutdownHookCallbacks {
 
     private final List<ShutdownHookCallback> callbacks = new LinkedList<>();
 
-    public ShutdownHookCallbacks() {
+    ShutdownHookCallbacks() {
         loadCallbacks();
     }
 
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
index 7a2f99c..c795ffd 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
@@ -27,6 +27,7 @@ import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
+import static java.util.Collections.emptyMap;
 import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
 import static org.apache.dubbo.common.constants.CommonConstants.CLASSIFIER_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
@@ -50,9 +51,11 @@ import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL
 import static org.apache.dubbo.common.constants.RegistryConstants.OVERRIDE_PROTOCOL;
 import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
 import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL;
 import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL;
+import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
 
 public class UrlUtils {
 
@@ -491,6 +494,31 @@ public class UrlUtils {
     }
 
     /**
+     * The specified {@link URL} is service discovery registry type or not
+     *
+     * @param url the {@link URL} connects to the registry
+     * @return If it is, return <code>true</code>, or <code>false</code>
+     * @since 2.7.4
+     */
+    public static boolean isServiceDiscoveryRegistryType(URL url) {
+        return isServiceDiscoveryRegistryType(url == null ? emptyMap() : url.getParameters());
+    }
+
+    /**
+     * The specified parameters of {@link URL} is service discovery registry type or not
+     *
+     * @param parameters the parameters of {@link URL} that connects to the registry
+     * @return If it is, return <code>true</code>, or <code>false</code>
+     * @since 2.7.4
+     */
+    public static boolean isServiceDiscoveryRegistryType(Map<String, String> parameters) {
+        if (parameters == null || parameters.isEmpty()) {
+            return false;
+        }
+        return SERVICE_REGISTRY_TYPE.equals(parameters.get(REGISTRY_TYPE_KEY));
+    }
+
+    /**
      * Check if the given value matches the given pattern. The pattern supports wildcard "*".
      *
      * @param pattern pattern
@@ -507,7 +535,7 @@ public class UrlUtils {
 
     /**
      * @param serviceKey, {group}/{interfaceName}:{version}
-     * @return  [group, interfaceName, version]
+     * @return [group, interfaceName, version]
      */
     public static String[] parseServiceKey(String serviceKey) {
         String[] arr = new String[3];
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/InmemoryConfigurationTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/InmemoryConfigurationTest.java
index bdb371a..9cdfcdd 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/InmemoryConfigurationTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/InmemoryConfigurationTest.java
@@ -29,7 +29,7 @@ import java.util.Map;
  */
 class InmemoryConfigurationTest {
 
-    private static InmemoryConfiguration memConfig;
+    private InmemoryConfiguration memConfig;
     private static final String MOCK_KEY = "mockKey";
     private static final String MOCK_VALUE = "mockValue";
     private static final String MOCK_ONE_KEY = "one";
@@ -59,7 +59,6 @@ class InmemoryConfigurationTest {
         Assertions.assertEquals(MOCK_VALUE, memConfig.getInternalProperty(MOCK_KEY));
         Assertions.assertEquals(MOCK_VALUE, memConfig.getString(MOCK_KEY, MOCK_VALUE));
         Assertions.assertEquals(MOCK_VALUE, memConfig.getProperty(MOCK_KEY, MOCK_VALUE));
-
     }
 
     /**
@@ -84,11 +83,27 @@ class InmemoryConfigurationTest {
 
     }
 
+    @Test
+    public void testGetInt() {
+        memConfig.addProperty("a", "1");
+        Assertions.assertEquals(1, memConfig.getInt("a"));
+        Assertions.assertEquals(Integer.valueOf(1), memConfig.getInteger("a", 2));
+        Assertions.assertEquals(2, memConfig.getInt("b", 2));
+    }
+
+    @Test
+    public void getBoolean() {
+        memConfig.addProperty("a", Boolean.TRUE.toString());
+        Assertions.assertTrue(memConfig.getBoolean("a"));
+        Assertions.assertFalse(memConfig.getBoolean("b", false));
+        Assertions.assertTrue(memConfig.getBoolean("b", Boolean.TRUE));
+    }
+
     /**
      * Clean.
      */
     @AfterEach
-    public void clean(){
+    public void clean() {
 
     }
 
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationFactoryTest.java
new file mode 100644
index 0000000..149d198
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationFactoryTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.config.configcenter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.nop.NopDynamicConfiguration;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link AbstractDynamicConfigurationFactory} Test
+ *
+ * @see AbstractDynamicConfigurationFactory
+ * @since 2.7.4
+ */
+public class AbstractDynamicConfigurationFactoryTest {
+
+    private AbstractDynamicConfigurationFactory factory;
+
+    @BeforeEach
+    public void init() {
+        factory = new AbstractDynamicConfigurationFactory() {
+            @Override
+            protected DynamicConfiguration createDynamicConfiguration(URL url) {
+                return new NopDynamicConfiguration(url);
+            }
+        };
+    }
+
+    @Test
+    public void testGetDynamicConfiguration() {
+        URL url = URL.valueOf("nop://127.0.0.1");
+        assertEquals(factory.getDynamicConfiguration(url), factory.getDynamicConfiguration(url));
+    }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationTest.java
new file mode 100644
index 0000000..42a15f9
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/AbstractDynamicConfigurationTest.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.config.configcenter;
+
+import org.apache.dubbo.common.URL;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.DEFAULT_THREAD_POOL_KEEP_ALIVE_TIME;
+import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.DEFAULT_THREAD_POOL_PREFIX;
+import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.DEFAULT_THREAD_POOL_SIZE;
+import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.PARAM_NAME_PREFIX;
+import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME;
+import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.THREAD_POOL_PREFIX_PARAM_NAME;
+import static org.apache.dubbo.common.config.configcenter.AbstractDynamicConfiguration.THREAD_POOL_SIZE_PARAM_NAME;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * {@link AbstractDynamicConfiguration} Test
+ *
+ * @since 2.7.4
+ */
+public class AbstractDynamicConfigurationTest {
+
+    private AbstractDynamicConfiguration configuration;
+
+    @BeforeEach
+    public void init() {
+        configuration = new AbstractDynamicConfiguration() {
+            @Override
+            protected String doGetConfig(String key, String group) throws Exception {
+                return null;
+            }
+
+            @Override
+            protected void doClose() throws Exception {
+
+            }
+        };
+    }
+
+    @Test
+    public void testConstants() {
+        assertEquals("dubbo.config-center.", PARAM_NAME_PREFIX);
+        assertEquals("dubbo.config-center.workers", DEFAULT_THREAD_POOL_PREFIX);
+        assertEquals("dubbo.config-center.thread-pool.prefix", THREAD_POOL_PREFIX_PARAM_NAME);
+        assertEquals("dubbo.config-center.thread-pool.size", THREAD_POOL_SIZE_PARAM_NAME);
+        assertEquals("dubbo.config-center.thread-pool.keep-alive-time", THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME);
+        assertEquals(1, DEFAULT_THREAD_POOL_SIZE);
+        assertEquals(60 * 1000, DEFAULT_THREAD_POOL_KEEP_ALIVE_TIME);
+    }
+
+    @Test
+    public void testConstructor() {
+        URL url = URL.valueOf("default://")
+                .addParameter(THREAD_POOL_PREFIX_PARAM_NAME, "test")
+                .addParameter(THREAD_POOL_SIZE_PARAM_NAME, 10)
+                .addParameter(THREAD_POOL_KEEP_ALIVE_TIME_PARAM_NAME, 100);
+
+        AbstractDynamicConfiguration configuration = new AbstractDynamicConfiguration(url) {
+
+            @Override
+            protected String doGetConfig(String key, String group) throws Exception {
+                return null;
+            }
+
+            @Override
+            protected void doClose() throws Exception {
+
+            }
+        };
+
+        ThreadPoolExecutor threadPoolExecutor = configuration.getWorkersThreadPool();
+        ThreadFactory threadFactory = threadPoolExecutor.getThreadFactory();
+
+        Thread thread = threadFactory.newThread(() -> {
+        });
+
+        assertEquals(10, threadPoolExecutor.getCorePoolSize());
+        assertEquals(10, threadPoolExecutor.getMaximumPoolSize());
+        assertEquals(100, threadPoolExecutor.getKeepAliveTime(TimeUnit.MILLISECONDS));
+        assertEquals("test-thread-1", thread.getName());
+    }
+
+    @Test
+    public void testPublishConfig() {
+        assertThrows(UnsupportedOperationException.class, () -> configuration.publishConfig(null, null), "No support");
+        assertThrows(UnsupportedOperationException.class, () -> configuration.publishConfig(null, null, null), "No support");
+    }
+
+    @Test
+    public void testRemoveConfig() {
+        assertThrows(UnsupportedOperationException.class, () -> configuration.removeConfig(null), "No support");
+        assertThrows(UnsupportedOperationException.class, () -> configuration.removeConfig(null, null), "No support");
+    }
+
+    @Test
+    public void testGetConfigGroups() {
+        assertThrows(UnsupportedOperationException.class, () -> configuration.getConfigGroups(), "No support");
+    }
+
+    @Test
+    public void testGetConfigKeys() {
+        assertThrows(UnsupportedOperationException.class, () -> configuration.getConfigKeys(null), "No support");
+    }
+
+    @Test
+    public void testGetConfigs() {
+        assertThrows(UnsupportedOperationException.class, () -> configuration.getConfigs(null), "No support");
+        assertThrows(UnsupportedOperationException.class, () -> configuration.getConfigs(null, 1000), "No support");
+    }
+
+    @Test
+    public void testGetConfig() {
+        assertNull(configuration.getConfig(null, null));
+        assertNull(configuration.getConfig(null, null, 200));
+    }
+
+    @Test
+    public void testGetInternalProperty() {
+        assertNull(configuration.getInternalProperty(null));
+    }
+
+    @Test
+    public void testGetProperties() {
+        assertNull(configuration.getProperties(null, null));
+        assertNull(configuration.getProperties(null, null, 100L));
+    }
+
+    @Test
+    public void testAddListener() {
+        configuration.addListener(null, null);
+        configuration.addListener(null, null, null);
+    }
+
+    @Test
+    public void testRemoveListener() {
+        configuration.removeListener(null, null);
+        configuration.removeListener(null, null, null);
+    }
+
+    @Test
+    public void testClose() throws Exception {
+        configuration.close();
+    }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangeTypeTest.java
similarity index 56%
copy from dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangeTypeTest.java
index 74ca3f3..8095d59 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangeTypeTest.java
@@ -14,27 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config.spring.context.annotation;
+package org.apache.dubbo.common.config.configcenter;
 
-import org.springframework.context.Lifecycle;
-import org.springframework.context.annotation.Import;
+import org.junit.jupiter.api.Test;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import static org.apache.dubbo.common.config.configcenter.ConfigChangeType.ADDED;
+import static org.apache.dubbo.common.config.configcenter.ConfigChangeType.DELETED;
+import static org.apache.dubbo.common.config.configcenter.ConfigChangeType.MODIFIED;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 
 /**
- * Enables Dubbo {@link Lifecycle} components
+ * {@link ConfigChangeType} Test
  *
+ * @see ConfigChangeType
  * @since 2.7.4
  */
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-@Inherited
-@Documented
-@Import(DubboLifecycleComponentRegistrar.class)
-public @interface EnableDubboLifecycle {
+public class ConfigChangeTypeTest {
+
+    @Test
+    public void testMembers() {
+        assertArrayEquals(new ConfigChangeType[]{ADDED, MODIFIED, DELETED}, ConfigChangeType.values());
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java
new file mode 100644
index 0000000..7de409a
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/ConfigChangedEventTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.config.configcenter;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * {@link ConfigChangedEvent} Test
+ *
+ * @since 2.7.4
+ */
+public class ConfigChangedEventTest {
+
+    private static final String key = "k";
+
+    private static final String group = "g";
+
+    private static final String content = "c";
+
+    @Test
+    public void testGetter() {
+
+        ConfigChangedEvent event = new ConfigChangedEvent(key, group, content);
+
+        assertEquals(key, event.getKey());
+        assertEquals(group, event.getGroup());
+        assertEquals(content, event.getContent());
+        assertEquals(ConfigChangeType.MODIFIED, event.getChangeType());
+        assertEquals("k,g", event.getSource());
+
+        event = new ConfigChangedEvent(key, group, content, ConfigChangeType.ADDED);
+
+        assertEquals(key, event.getKey());
+        assertEquals(group, event.getGroup());
+        assertEquals(content, event.getContent());
+        assertEquals(ConfigChangeType.ADDED, event.getChangeType());
+        assertEquals("k,g", event.getSource());
+    }
+
+    @Test
+    public void testEqualsAndHashCode() {
+        for (ConfigChangeType type : ConfigChangeType.values()) {
+            assertEquals(new ConfigChangedEvent(key, group, content, type), new ConfigChangedEvent(key, group, content, type));
+            assertEquals(new ConfigChangedEvent(key, group, content, type).hashCode(), new ConfigChangedEvent(key, group, content, type).hashCode());
+            assertEquals(new ConfigChangedEvent(key, group, content, type).toString(), new ConfigChangedEvent(key, group, content, type).toString());
+        }
+    }
+
+    @Test
+    public void testToString() {
+        ConfigChangedEvent event = new ConfigChangedEvent(key, group, content);
+        assertNotNull(event.toString());
+    }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationFactoryTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationFactoryTest.java
new file mode 100644
index 0000000..ccd368d
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationFactoryTest.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.config.configcenter.file;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link FileSystemDynamicConfigurationFactory} Test
+ *
+ * @since 2.7.4
+ */
+public class FileSystemDynamicConfigurationFactoryTest {
+
+    @Test
+    public void testGetFactory() {
+        DynamicConfigurationFactory factory = DynamicConfigurationFactory.getDynamicConfigurationFactory("not-exists");
+        assertEquals(factory, DynamicConfigurationFactory.getDynamicConfigurationFactory("file"));
+        assertEquals(factory.getDynamicConfiguration(URL.valueOf("dummy")), factory.getDynamicConfiguration(URL.valueOf("dummy")));
+        assertEquals(FileSystemDynamicConfiguration.class, factory.getDynamicConfiguration(URL.valueOf("dummy")).getClass());
+    }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java
index 17ead90..7140136 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/config/configcenter/file/FileSystemDynamicConfigurationTest.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.common.config.configcenter.file;
 import org.apache.dubbo.common.URL;
 
 import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -49,12 +50,18 @@ public class FileSystemDynamicConfigurationTest {
 
     @BeforeEach
     public void init() {
-        String classPath = getClassPath();
-        URL url = valueOf("dubbo://127.0.0.1:20880").addParameter(CONFIG_CENTER_DIR_PARAM_NAME, classPath + File.separator + "config-center");
+        File rootDirectory = new File(getClassPath(), "config-center");
+        rootDirectory.mkdirs();
+        URL url = valueOf("dubbo://127.0.0.1:20880").addParameter(CONFIG_CENTER_DIR_PARAM_NAME, rootDirectory.getAbsolutePath());
         configuration = new FileSystemDynamicConfiguration(url);
         deleteQuietly(configuration.getRootDirectory());
     }
 
+    @AfterEach
+    public void destroy() throws Exception {
+        configuration.close();
+    }
+
     private String getClassPath() {
         return getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
     }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/AdaptiveClassCodeGeneratorTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/AdaptiveClassCodeGeneratorTest.java
new file mode 100644
index 0000000..911e34b
--- /dev/null
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/AdaptiveClassCodeGeneratorTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.extension;
+
+import org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt;
+import org.apache.dubbo.common.utils.IOUtils;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link AdaptiveClassCodeGenerator} Test
+ *
+ * @since 2.7.4
+ */
+public class AdaptiveClassCodeGeneratorTest {
+
+    @Test
+    public void testGenerate() throws IOException {
+        AdaptiveClassCodeGenerator generator = new AdaptiveClassCodeGenerator(HasAdaptiveExt.class, "adaptive");
+        String value = generator.generate();
+        URL url = getClass().getResource("/org/apache/dubbo/common/extension/adaptive/HasAdaptiveExt$Adaptive");
+        try (InputStream inputStream = url.openStream()) {
+            String content = IOUtils.read(new InputStreamReader(inputStream, "UTF-8"));
+            assertEquals(content, value);
+        }
+    }
+}
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java
index ffe542a..393ae3a 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/extension/ExtensionLoaderTest.java
@@ -53,11 +53,13 @@ import org.apache.dubbo.common.extension.injection.impl.InjectExtImpl;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
 import static org.hamcrest.CoreMatchers.allOf;
 import static org.hamcrest.CoreMatchers.anyOf;
 import static org.hamcrest.CoreMatchers.instanceOf;
@@ -73,7 +75,7 @@ public class ExtensionLoaderTest {
     @Test
     public void test_getExtensionLoader_Null() throws Exception {
         try {
-            ExtensionLoader.getExtensionLoader(null);
+            getExtensionLoader(null);
             fail();
         } catch (IllegalArgumentException expected) {
             assertThat(expected.getMessage(),
@@ -84,7 +86,7 @@ public class ExtensionLoaderTest {
     @Test
     public void test_getExtensionLoader_NotInterface() throws Exception {
         try {
-            ExtensionLoader.getExtensionLoader(ExtensionLoaderTest.class);
+            getExtensionLoader(ExtensionLoaderTest.class);
             fail();
         } catch (IllegalArgumentException expected) {
             assertThat(expected.getMessage(),
@@ -95,7 +97,7 @@ public class ExtensionLoaderTest {
     @Test
     public void test_getExtensionLoader_NotSpiAnnotation() throws Exception {
         try {
-            ExtensionLoader.getExtensionLoader(NoSpiExt.class);
+            getExtensionLoader(NoSpiExt.class);
             fail();
         } catch (IllegalArgumentException expected) {
             assertThat(expected.getMessage(),
@@ -107,34 +109,34 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_getDefaultExtension() throws Exception {
-        SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getDefaultExtension();
+        SimpleExt ext = getExtensionLoader(SimpleExt.class).getDefaultExtension();
         assertThat(ext, instanceOf(SimpleExtImpl1.class));
 
-        String name = ExtensionLoader.getExtensionLoader(SimpleExt.class).getDefaultExtensionName();
+        String name = getExtensionLoader(SimpleExt.class).getDefaultExtensionName();
         assertEquals("impl1", name);
     }
 
     @Test
     public void test_getDefaultExtension_NULL() throws Exception {
-        Ext2 ext = ExtensionLoader.getExtensionLoader(Ext2.class).getDefaultExtension();
+        Ext2 ext = getExtensionLoader(Ext2.class).getDefaultExtension();
         assertNull(ext);
 
-        String name = ExtensionLoader.getExtensionLoader(Ext2.class).getDefaultExtensionName();
+        String name = getExtensionLoader(Ext2.class).getDefaultExtensionName();
         assertNull(name);
     }
 
     @Test
     public void test_getExtension() throws Exception {
-        assertTrue(ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension("impl1") instanceof SimpleExtImpl1);
-        assertTrue(ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension("impl2") instanceof SimpleExtImpl2);
+        assertTrue(getExtensionLoader(SimpleExt.class).getExtension("impl1") instanceof SimpleExtImpl1);
+        assertTrue(getExtensionLoader(SimpleExt.class).getExtension("impl2") instanceof SimpleExtImpl2);
     }
 
     @Test
     public void test_getExtension_WithWrapper() throws Exception {
-        WrappedExt impl1 = ExtensionLoader.getExtensionLoader(WrappedExt.class).getExtension("impl1");
+        WrappedExt impl1 = getExtensionLoader(WrappedExt.class).getExtension("impl1");
         assertThat(impl1, anyOf(instanceOf(Ext5Wrapper1.class), instanceOf(Ext5Wrapper2.class)));
 
-        WrappedExt impl2 = ExtensionLoader.getExtensionLoader(WrappedExt.class).getExtension("impl2");
+        WrappedExt impl2 = getExtensionLoader(WrappedExt.class).getExtension("impl2");
         assertThat(impl2, anyOf(instanceOf(Ext5Wrapper1.class), instanceOf(Ext5Wrapper2.class)));
 
 
@@ -150,7 +152,7 @@ public class ExtensionLoaderTest {
     @Test
     public void test_getExtension_ExceptionNoExtension() throws Exception {
         try {
-            ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension("XXX");
+            getExtensionLoader(SimpleExt.class).getExtension("XXX");
             fail();
         } catch (IllegalStateException expected) {
             assertThat(expected.getMessage(), containsString("No such extension org.apache.dubbo.common.extension.ext1.SimpleExt by name XXX"));
@@ -160,7 +162,7 @@ public class ExtensionLoaderTest {
     @Test
     public void test_getExtension_ExceptionNoExtension_WrapperNotAffactName() throws Exception {
         try {
-            ExtensionLoader.getExtensionLoader(WrappedExt.class).getExtension("XXX");
+            getExtensionLoader(WrappedExt.class).getExtension("XXX");
             fail();
         } catch (IllegalStateException expected) {
             assertThat(expected.getMessage(), containsString("No such extension org.apache.dubbo.common.extension.ext6_wrap.WrappedExt by name XXX"));
@@ -170,7 +172,7 @@ public class ExtensionLoaderTest {
     @Test
     public void test_getExtension_ExceptionNullArg() throws Exception {
         try {
-            ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension(null);
+            getExtensionLoader(SimpleExt.class).getExtension(null);
             fail();
         } catch (IllegalArgumentException expected) {
             assertThat(expected.getMessage(), containsString("Extension name == null"));
@@ -179,12 +181,12 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_hasExtension() throws Exception {
-        assertTrue(ExtensionLoader.getExtensionLoader(SimpleExt.class).hasExtension("impl1"));
-        assertFalse(ExtensionLoader.getExtensionLoader(SimpleExt.class).hasExtension("impl1,impl2"));
-        assertFalse(ExtensionLoader.getExtensionLoader(SimpleExt.class).hasExtension("xxx"));
+        assertTrue(getExtensionLoader(SimpleExt.class).hasExtension("impl1"));
+        assertFalse(getExtensionLoader(SimpleExt.class).hasExtension("impl1,impl2"));
+        assertFalse(getExtensionLoader(SimpleExt.class).hasExtension("xxx"));
 
         try {
-            ExtensionLoader.getExtensionLoader(SimpleExt.class).hasExtension(null);
+            getExtensionLoader(SimpleExt.class).hasExtension(null);
             fail();
         } catch (IllegalArgumentException expected) {
             assertThat(expected.getMessage(), containsString("Extension name == null"));
@@ -193,14 +195,14 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_hasExtension_wrapperIsNotExt() throws Exception {
-        assertTrue(ExtensionLoader.getExtensionLoader(WrappedExt.class).hasExtension("impl1"));
-        assertFalse(ExtensionLoader.getExtensionLoader(WrappedExt.class).hasExtension("impl1,impl2"));
-        assertFalse(ExtensionLoader.getExtensionLoader(WrappedExt.class).hasExtension("xxx"));
+        assertTrue(getExtensionLoader(WrappedExt.class).hasExtension("impl1"));
+        assertFalse(getExtensionLoader(WrappedExt.class).hasExtension("impl1,impl2"));
+        assertFalse(getExtensionLoader(WrappedExt.class).hasExtension("xxx"));
 
-        assertFalse(ExtensionLoader.getExtensionLoader(WrappedExt.class).hasExtension("wrapper1"));
+        assertFalse(getExtensionLoader(WrappedExt.class).hasExtension("wrapper1"));
 
         try {
-            ExtensionLoader.getExtensionLoader(WrappedExt.class).hasExtension(null);
+            getExtensionLoader(WrappedExt.class).hasExtension(null);
             fail();
         } catch (IllegalArgumentException expected) {
             assertThat(expected.getMessage(), containsString("Extension name == null"));
@@ -209,7 +211,7 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_getSupportedExtensions() throws Exception {
-        Set<String> exts = ExtensionLoader.getExtensionLoader(SimpleExt.class).getSupportedExtensions();
+        Set<String> exts = getExtensionLoader(SimpleExt.class).getSupportedExtensions();
 
         Set<String> expected = new HashSet<String>();
         expected.add("impl1");
@@ -221,7 +223,7 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_getSupportedExtensions_wrapperIsNotExt() throws Exception {
-        Set<String> exts = ExtensionLoader.getExtensionLoader(WrappedExt.class).getSupportedExtensions();
+        Set<String> exts = getExtensionLoader(WrappedExt.class).getSupportedExtensions();
 
         Set<String> expected = new HashSet<String>();
         expected.add("impl1");
@@ -233,35 +235,35 @@ public class ExtensionLoaderTest {
     @Test
     public void test_AddExtension() throws Exception {
         try {
-            ExtensionLoader.getExtensionLoader(AddExt1.class).getExtension("Manual1");
+            getExtensionLoader(AddExt1.class).getExtension("Manual1");
             fail();
         } catch (IllegalStateException expected) {
             assertThat(expected.getMessage(), containsString("No such extension org.apache.dubbo.common.extension.ext8_add.AddExt1 by name Manual"));
         }
 
-        ExtensionLoader.getExtensionLoader(AddExt1.class).addExtension("Manual1", AddExt1_ManualAdd1.class);
-        AddExt1 ext = ExtensionLoader.getExtensionLoader(AddExt1.class).getExtension("Manual1");
+        getExtensionLoader(AddExt1.class).addExtension("Manual1", AddExt1_ManualAdd1.class);
+        AddExt1 ext = getExtensionLoader(AddExt1.class).getExtension("Manual1");
 
         assertThat(ext, instanceOf(AddExt1_ManualAdd1.class));
-        assertEquals("Manual1", ExtensionLoader.getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd1.class));
+        assertEquals("Manual1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd1.class));
     }
 
     @Test
     public void test_AddExtension_NoExtend() throws Exception {
 //        ExtensionLoader.getExtensionLoader(Ext9Empty.class).getSupportedExtensions();
-        ExtensionLoader.getExtensionLoader(Ext9Empty.class).addExtension("ext9", Ext9EmptyImpl.class);
-        Ext9Empty ext = ExtensionLoader.getExtensionLoader(Ext9Empty.class).getExtension("ext9");
+        getExtensionLoader(Ext9Empty.class).addExtension("ext9", Ext9EmptyImpl.class);
+        Ext9Empty ext = getExtensionLoader(Ext9Empty.class).getExtension("ext9");
 
         assertThat(ext, instanceOf(Ext9Empty.class));
-        assertEquals("ext9", ExtensionLoader.getExtensionLoader(Ext9Empty.class).getExtensionName(Ext9EmptyImpl.class));
+        assertEquals("ext9", getExtensionLoader(Ext9Empty.class).getExtensionName(Ext9EmptyImpl.class));
     }
 
     @Test
     public void test_AddExtension_ExceptionWhenExistedExtension() throws Exception {
-        SimpleExt ext = ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension("impl1");
+        SimpleExt ext = getExtensionLoader(SimpleExt.class).getExtension("impl1");
 
         try {
-            ExtensionLoader.getExtensionLoader(AddExt1.class).addExtension("impl1", AddExt1_ManualAdd1.class);
+            getExtensionLoader(AddExt1.class).addExtension("impl1", AddExt1_ManualAdd1.class);
             fail();
         } catch (IllegalStateException expected) {
             assertThat(expected.getMessage(), containsString("Extension name impl1 already exists (Extension interface org.apache.dubbo.common.extension.ext8_add.AddExt1)!"));
@@ -270,7 +272,7 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_AddExtension_Adaptive() throws Exception {
-        ExtensionLoader<AddExt2> loader = ExtensionLoader.getExtensionLoader(AddExt2.class);
+        ExtensionLoader<AddExt2> loader = getExtensionLoader(AddExt2.class);
         loader.addExtension(null, AddExt2_ManualAdaptive.class);
 
         AddExt2 adaptive = loader.getAdaptiveExtension();
@@ -279,7 +281,7 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_AddExtension_Adaptive_ExceptionWhenExistedAdaptive() throws Exception {
-        ExtensionLoader<AddExt1> loader = ExtensionLoader.getExtensionLoader(AddExt1.class);
+        ExtensionLoader<AddExt1> loader = getExtensionLoader(AddExt1.class);
 
         loader.getAdaptiveExtension();
 
@@ -294,30 +296,30 @@ public class ExtensionLoaderTest {
     @Test
     public void test_replaceExtension() throws Exception {
         try {
-            ExtensionLoader.getExtensionLoader(AddExt1.class).getExtension("Manual2");
+            getExtensionLoader(AddExt1.class).getExtension("Manual2");
             fail();
         } catch (IllegalStateException expected) {
             assertThat(expected.getMessage(), containsString("No such extension org.apache.dubbo.common.extension.ext8_add.AddExt1 by name Manual"));
         }
 
         {
-            AddExt1 ext = ExtensionLoader.getExtensionLoader(AddExt1.class).getExtension("impl1");
+            AddExt1 ext = getExtensionLoader(AddExt1.class).getExtension("impl1");
 
             assertThat(ext, instanceOf(AddExt1Impl1.class));
-            assertEquals("impl1", ExtensionLoader.getExtensionLoader(AddExt1.class).getExtensionName(AddExt1Impl1.class));
+            assertEquals("impl1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1Impl1.class));
         }
         {
-            ExtensionLoader.getExtensionLoader(AddExt1.class).replaceExtension("impl1", AddExt1_ManualAdd2.class);
-            AddExt1 ext = ExtensionLoader.getExtensionLoader(AddExt1.class).getExtension("impl1");
+            getExtensionLoader(AddExt1.class).replaceExtension("impl1", AddExt1_ManualAdd2.class);
+            AddExt1 ext = getExtensionLoader(AddExt1.class).getExtension("impl1");
 
             assertThat(ext, instanceOf(AddExt1_ManualAdd2.class));
-            assertEquals("impl1", ExtensionLoader.getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd2.class));
+            assertEquals("impl1", getExtensionLoader(AddExt1.class).getExtensionName(AddExt1_ManualAdd2.class));
         }
     }
 
     @Test
     public void test_replaceExtension_Adaptive() throws Exception {
-        ExtensionLoader<AddExt3> loader = ExtensionLoader.getExtensionLoader(AddExt3.class);
+        ExtensionLoader<AddExt3> loader = getExtensionLoader(AddExt3.class);
 
         AddExt3 adaptive = loader.getAdaptiveExtension();
         assertFalse(adaptive instanceof AddExt3_ManualAdaptive);
@@ -330,10 +332,10 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_replaceExtension_ExceptionWhenNotExistedExtension() throws Exception {
-        AddExt1 ext = ExtensionLoader.getExtensionLoader(AddExt1.class).getExtension("impl1");
+        AddExt1 ext = getExtensionLoader(AddExt1.class).getExtension("impl1");
 
         try {
-            ExtensionLoader.getExtensionLoader(AddExt1.class).replaceExtension("NotExistedExtension", AddExt1_ManualAdd1.class);
+            getExtensionLoader(AddExt1.class).replaceExtension("NotExistedExtension", AddExt1_ManualAdd1.class);
             fail();
         } catch (IllegalStateException expected) {
             assertThat(expected.getMessage(), containsString("Extension name NotExistedExtension doesn't exist (Extension interface org.apache.dubbo.common.extension.ext8_add.AddExt1)"));
@@ -342,7 +344,7 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_replaceExtension_Adaptive_ExceptionWhenNotExistedExtension() throws Exception {
-        ExtensionLoader<AddExt4> loader = ExtensionLoader.getExtensionLoader(AddExt4.class);
+        ExtensionLoader<AddExt4> loader = getExtensionLoader(AddExt4.class);
 
         try {
             loader.replaceExtension(null, AddExt4_ManualAdaptive.class);
@@ -354,7 +356,7 @@ public class ExtensionLoaderTest {
 
     @Test
     public void test_InitError() throws Exception {
-        ExtensionLoader<InitErrorExt> loader = ExtensionLoader.getExtensionLoader(InitErrorExt.class);
+        ExtensionLoader<InitErrorExt> loader = getExtensionLoader(InitErrorExt.class);
 
         loader.getExtension("ok");
 
@@ -371,21 +373,21 @@ public class ExtensionLoaderTest {
     public void testLoadActivateExtension() throws Exception {
         // test default
         URL url = URL.valueOf("test://localhost/test");
-        List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+        List<ActivateExt1> list = getExtensionLoader(ActivateExt1.class)
                 .getActivateExtension(url, new String[]{}, "default_group");
         Assertions.assertEquals(1, list.size());
         Assertions.assertSame(list.get(0).getClass(), ActivateExt1Impl1.class);
 
         // test group
         url = url.addParameter(GROUP_KEY, "group1");
-        list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+        list = getExtensionLoader(ActivateExt1.class)
                 .getActivateExtension(url, new String[]{}, "group1");
         Assertions.assertEquals(1, list.size());
         Assertions.assertSame(list.get(0).getClass(), GroupActivateExtImpl.class);
 
         // test old @Activate group
         url = url.addParameter(GROUP_KEY, "old_group");
-        list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+        list = getExtensionLoader(ActivateExt1.class)
                 .getActivateExtension(url, new String[]{}, "old_group");
         Assertions.assertEquals(2, list.size());
         Assertions.assertTrue(list.get(0).getClass() == OldActivateExt1Impl2.class
@@ -395,7 +397,7 @@ public class ExtensionLoaderTest {
         url = url.removeParameter(GROUP_KEY);
         url = url.addParameter(GROUP_KEY, "value");
         url = url.addParameter("value", "value");
-        list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+        list = getExtensionLoader(ActivateExt1.class)
                 .getActivateExtension(url, new String[]{}, "value");
         Assertions.assertEquals(1, list.size());
         Assertions.assertSame(list.get(0).getClass(), ValueActivateExtImpl.class);
@@ -403,7 +405,7 @@ public class ExtensionLoaderTest {
         // test order
         url = URL.valueOf("test://localhost/test");
         url = url.addParameter(GROUP_KEY, "order");
-        list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+        list = getExtensionLoader(ActivateExt1.class)
                 .getActivateExtension(url, new String[]{}, "order");
         Assertions.assertEquals(2, list.size());
         Assertions.assertSame(list.get(0).getClass(), OrderActivateExtImpl1.class);
@@ -414,14 +416,14 @@ public class ExtensionLoaderTest {
     public void testLoadDefaultActivateExtension() throws Exception {
         // test default
         URL url = URL.valueOf("test://localhost/test?ext=order1,default");
-        List<ActivateExt1> list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+        List<ActivateExt1> list = getExtensionLoader(ActivateExt1.class)
                 .getActivateExtension(url, "ext", "default_group");
         Assertions.assertEquals(2, list.size());
         Assertions.assertSame(list.get(0).getClass(), OrderActivateExtImpl1.class);
         Assertions.assertSame(list.get(1).getClass(), ActivateExt1Impl1.class);
 
         url = URL.valueOf("test://localhost/test?ext=default,order1");
-        list = ExtensionLoader.getExtensionLoader(ActivateExt1.class)
+        list = getExtensionLoader(ActivateExt1.class)
                 .getActivateExtension(url, "ext", "default_group");
         Assertions.assertEquals(2, list.size());
         Assertions.assertSame(list.get(0).getClass(), ActivateExt1Impl1.class);
@@ -431,7 +433,7 @@ public class ExtensionLoaderTest {
     @Test
     public void testInjectExtension() {
         // test default
-        InjectExt injectExt = ExtensionLoader.getExtensionLoader(InjectExt.class).getExtension("injection");
+        InjectExt injectExt = getExtensionLoader(InjectExt.class).getExtension("injection");
         InjectExtImpl injectExtImpl = (InjectExtImpl) injectExt;
         Assertions.assertNotNull(injectExtImpl.getSimpleExt());
         Assertions.assertNull(injectExtImpl.getSimpleExt1());
@@ -440,13 +442,28 @@ public class ExtensionLoaderTest {
 
     @Test
     void testMultiNames() {
-        Ext10MultiNames ext10MultiNames = ExtensionLoader.getExtensionLoader(Ext10MultiNames.class).getExtension("impl");
+        Ext10MultiNames ext10MultiNames = getExtensionLoader(Ext10MultiNames.class).getExtension("impl");
         Assertions.assertNotNull(ext10MultiNames);
-        ext10MultiNames = ExtensionLoader.getExtensionLoader(Ext10MultiNames.class).getExtension("implMultiName");
+        ext10MultiNames = getExtensionLoader(Ext10MultiNames.class).getExtension("implMultiName");
         Assertions.assertNotNull(ext10MultiNames);
         Assertions.assertThrows(
                 IllegalStateException.class,
-                () -> ExtensionLoader.getExtensionLoader(Ext10MultiNames.class).getExtension("impl,implMultiName")
+                () -> getExtensionLoader(Ext10MultiNames.class).getExtension("impl,implMultiName")
         );
     }
+
+    @Test
+    public void testGetOrDefaultExtension() {
+        ExtensionLoader<InjectExt> loader = getExtensionLoader(InjectExt.class);
+        InjectExt injectExt = loader.getOrDefaultExtension("non-exists");
+        assertEquals(InjectExtImpl.class, injectExt.getClass());
+        assertEquals(InjectExtImpl.class, loader.getOrDefaultExtension("injection").getClass());
+    }
+
+    @Test
+    public void testGetSupported() {
+        ExtensionLoader<InjectExt> loader = getExtensionLoader(InjectExt.class);
+        assertEquals(1, loader.getSupportedExtensions().size());
+        assertEquals(Collections.singleton("injection"), loader.getSupportedExtensions());
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableActionTest.java
similarity index 65%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableActionTest.java
index 9a1ea89..977b730 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableActionTest.java
@@ -14,12 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.lang;
+package org.apache.dubbo.common.function;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.function.ThrowableAction.execute;
 
 /**
- * {@link ShutdownHookCallbacks}
+ * {@link ThrowableAction} Test
  *
  * @since 2.7.4
  */
-public class ShutdownHookCallbacksTest {
+public class ThrowableActionTest {
+
+    @Test
+    public void testExecute() {
+        Assertions.assertThrows(RuntimeException.class, () -> execute(() -> {
+            throw new Exception("Test");
+        }), "Test");
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableConsumerTest.java
similarity index 63%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableConsumerTest.java
index 9a1ea89..475974c 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableConsumerTest.java
@@ -14,12 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.lang;
+package org.apache.dubbo.common.function;
+
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.function.ThrowableConsumer.execute;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 /**
- * {@link ShutdownHookCallbacks}
+ * {@link ThrowableConsumer} Test
  *
  * @since 2.7.4
  */
-public class ShutdownHookCallbacksTest {
+public class ThrowableConsumerTest {
+
+    @Test
+    public void testExecute() {
+        assertThrows(RuntimeException.class, () -> execute("Hello,World", m -> {
+            throw new Exception(m);
+        }), "Hello,World");
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableFunctionTest.java
similarity index 63%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableFunctionTest.java
index 9a1ea89..abd2bee 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/function/ThrowableFunctionTest.java
@@ -14,12 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.lang;
+package org.apache.dubbo.common.function;
+
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.function.ThrowableConsumer.execute;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 /**
- * {@link ShutdownHookCallbacks}
+ * {@link ThrowableFunction} Test
  *
  * @since 2.7.4
  */
-public class ShutdownHookCallbacksTest {
+public class ThrowableFunctionTest {
+
+    @Test
+    public void testExecute() {
+        assertThrows(RuntimeException.class, () -> execute("Hello,World", m -> {
+            throw new Exception(m);
+        }), "Hello,World");
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/lang/DefaultShutdownHookCallback.java
similarity index 73%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/lang/DefaultShutdownHookCallback.java
index 9a1ea89..35256c1 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/lang/DefaultShutdownHookCallback.java
@@ -17,9 +17,20 @@
 package org.apache.dubbo.common.lang;
 
 /**
- * {@link ShutdownHookCallbacks}
+ * Default {@link ShutdownHookCallback}
  *
  * @since 2.7.4
  */
-public class ShutdownHookCallbacksTest {
+public class DefaultShutdownHookCallback implements ShutdownHookCallback {
+
+    private boolean executed = false;
+
+    @Override
+    public void callback() throws Throwable {
+        executed = true;
+    }
+
+    public boolean isExecuted() {
+        return executed;
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/PrioritizedTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/lang/PrioritizedTest.java
index 3ee027f..3bb5753 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/PrioritizedTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/lang/PrioritizedTest.java
@@ -41,7 +41,7 @@ public class PrioritizedTest {
 
     @Test
     public void testGetPriority() {
-        assertEquals(Prioritized.MIN_PRIORITY, new Prioritized() {
+        assertEquals(Prioritized.NORMAL_PRIORITY, new Prioritized() {
         }.getPriority());
     }
 
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
index 9a1ea89..a2ab0b4 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
@@ -16,10 +16,42 @@
  */
 package org.apache.dubbo.common.lang;
 
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 /**
  * {@link ShutdownHookCallbacks}
  *
  * @since 2.7.4
  */
 public class ShutdownHookCallbacksTest {
+
+    private ShutdownHookCallbacks callbacks;
+
+    @BeforeEach
+    public void init() {
+        callbacks = new ShutdownHookCallbacks();
+    }
+
+    @Test
+    public void testSingleton() {
+        assertNotNull(callbacks);
+    }
+
+    @Test
+    public void testCallback() {
+        callbacks.callback();
+        DefaultShutdownHookCallback callback = (DefaultShutdownHookCallback) callbacks.getCallbacks().iterator().next();
+        assertTrue(callback.isExecuted());
+    }
+
+    @AfterEach
+    public void destroy() {
+        callbacks.clear();
+        assertTrue(callbacks.getCallbacks().isEmpty());
+    }
 }
diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultCharSequence.java
similarity index 62%
copy from dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultCharSequence.java
index 9a1ea89..e00d4ac 100644
--- a/dubbo-common/src/test/java/org/apache/dubbo/common/lang/ShutdownHookCallbacksTest.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DefaultCharSequence.java
@@ -14,12 +14,32 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.common.lang;
+package org.apache.dubbo.common.utils;
+
+import org.apache.dubbo.common.lang.Prioritized;
 
 /**
- * {@link ShutdownHookCallbacks}
+ * Default {@link CharSequence}
  *
  * @since 2.7.4
  */
-public class ShutdownHookCallbacksTest {
+public class DefaultCharSequence implements CharSequence, Prioritized {
+    @Override
+    public int length() {
+        return 0;
+    }
+
+    @Override
+    public char charAt(int index) {
+        return 0;
+    }
+
+    @Override
+    public CharSequence subSequence(int start, int end) {
+        return null;
+    }
+
+    public int getPriority() {
+        return MAX_PRIORITY;
+    }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DubboServiceLoaderTest.java
similarity index 55%
copy from dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
copy to dubbo-common/src/test/java/org/apache/dubbo/common/utils/DubboServiceLoaderTest.java
index 74ca3f3..3456d24 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
+++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/DubboServiceLoaderTest.java
@@ -14,27 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config.spring.context.annotation;
+package org.apache.dubbo.common.utils;
 
-import org.springframework.context.Lifecycle;
-import org.springframework.context.annotation.Import;
+import org.junit.jupiter.api.Test;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import java.util.List;
+
+import static org.apache.dubbo.common.utils.DubboServiceLoader.loadServices;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * Enables Dubbo {@link Lifecycle} components
+ * {@link DubboServiceLoader} Test
  *
  * @since 2.7.4
  */
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-@Inherited
-@Documented
-@Import(DubboLifecycleComponentRegistrar.class)
-public @interface EnableDubboLifecycle {
+public class DubboServiceLoaderTest {
+
+    @Test
+    public void testLoad() {
+        List<CharSequence> charSequences = loadServices(CharSequence.class);
+        assertEquals(DefaultCharSequence.class, charSequences.get(0).getClass());
+        assertEquals(String.class, charSequences.get(1).getClass());
+        assertEquals(StringBuilder.class, charSequences.get(2).getClass());
+    }
 }
diff --git a/dubbo-common/src/test/resources/META-INF/services/java.lang.CharSequence b/dubbo-common/src/test/resources/META-INF/services/java.lang.CharSequence
new file mode 100644
index 0000000..0fb6897
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/java.lang.CharSequence
@@ -0,0 +1,3 @@
+java.lang.String
+java.lang.StringBuilder
+org.apache.dubbo.common.utils.DefaultCharSequence
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.lang.ShutdownHookCallback b/dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.lang.ShutdownHookCallback
new file mode 100644
index 0000000..a09ea1e
--- /dev/null
+++ b/dubbo-common/src/test/resources/META-INF/services/org.apache.dubbo.common.lang.ShutdownHookCallback
@@ -0,0 +1 @@
+org.apache.dubbo.common.lang.DefaultShutdownHookCallback
\ No newline at end of file
diff --git a/dubbo-common/src/test/resources/org/apache/dubbo/common/extension/adaptive/HasAdaptiveExt$Adaptive b/dubbo-common/src/test/resources/org/apache/dubbo/common/extension/adaptive/HasAdaptiveExt$Adaptive
new file mode 100644
index 0000000..1ee9dc9
--- /dev/null
+++ b/dubbo-common/src/test/resources/org/apache/dubbo/common/extension/adaptive/HasAdaptiveExt$Adaptive
@@ -0,0 +1,12 @@
+package org.apache.dubbo.common.extension.adaptive;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+public class HasAdaptiveExt$Adaptive implements org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt {
+public java.lang.String echo(org.apache.dubbo.common.URL arg0, java.lang.String arg1)  {
+if (arg0 == null) throw new IllegalArgumentException("url == null");
+org.apache.dubbo.common.URL url = arg0;
+String extName = url.getParameter("has.adaptive.ext", "adaptive");
+if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt) name from url (" + url.toString() + ") use keys([has.adaptive.ext])");
+org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt extension = (org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt)ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt.class).getExtension(extName);
+return extension.echo(arg0, arg1);
+}
+}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
index 257cf5d..e0bb467 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java
@@ -59,10 +59,9 @@ import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_SE
 import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_PROTOCOL;
-import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_PROTOCOL;
-import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
 import static org.apache.dubbo.common.extension.ExtensionLoader.getExtensionLoader;
+import static org.apache.dubbo.common.utils.UrlUtils.isServiceDiscoveryRegistryType;
 import static org.apache.dubbo.config.Constants.DUBBO_IP_TO_REGISTRY;
 import static org.apache.dubbo.config.Constants.LAYER_KEY;
 import static org.apache.dubbo.config.Constants.LISTENER_KEY;
@@ -308,9 +307,7 @@ public abstract class AbstractInterfaceConfig extends AbstractMethodConfig {
     }
 
     private String extractRegistryType(URL url) {
-        boolean isServiceRegistry = url.getParameter(REGISTRY_TYPE_KEY) != null
-                && SERVICE_REGISTRY_TYPE.equals(url.getParameter(REGISTRY_TYPE_KEY));
-        return isServiceRegistry ? SERVICE_REGISTRY_PROTOCOL : REGISTRY_PROTOCOL;
+        return isServiceDiscoveryRegistryType(url) ? SERVICE_REGISTRY_PROTOCOL : REGISTRY_PROTOCOL;
     }
 
     /**
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizer.java
index 25c4d20..bac27b6 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizer.java
@@ -31,8 +31,7 @@ public class ServiceInstancePortCustomizer implements ServiceInstanceCustomizer
     @Override
     public void customize(ServiceInstance serviceInstance) {
 
-        if (serviceInstance.getPort() != null
-                || serviceInstance.getPort().intValue() < 1) {
+        if (serviceInstance.getPort() != null) {
             return;
         }
 
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/DubboServiceDestroyedEventTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/DubboServiceDestroyedEventTest.java
new file mode 100644
index 0000000..03b7cdd
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/DubboServiceDestroyedEventTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.event;
+
+import org.apache.dubbo.config.DubboShutdownHook;
+import org.apache.dubbo.event.EventDispatcher;
+import org.apache.dubbo.event.EventListener;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link DubboServiceDestroyedEvent} Test
+ *
+ * @since 2.7.4
+ */
+public class DubboServiceDestroyedEventTest implements EventListener<DubboServiceDestroyedEvent> {
+
+    private DubboServiceDestroyedEvent event;
+
+    private EventDispatcher eventDispatcher = EventDispatcher.getDefaultExtension();
+
+    private DubboShutdownHook dubboShutdownHook;
+
+    @BeforeEach
+    public void init() {
+        eventDispatcher.removeAllEventListeners();
+        eventDispatcher.addEventListener(this);
+        dubboShutdownHook = DubboShutdownHook.getDubboShutdownHook();
+    }
+
+    @Test
+    public void testOnEvent() {
+        dubboShutdownHook.doDestroy();
+        assertEquals(dubboShutdownHook, event.getSource());
+    }
+
+    @Override
+    public void onEvent(DubboServiceDestroyedEvent event) {
+        this.event = event;
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/DubboShutdownHookRegisteredEventTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/DubboShutdownHookRegisteredEventTest.java
new file mode 100644
index 0000000..ec2a850
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/DubboShutdownHookRegisteredEventTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.event;
+
+import org.apache.dubbo.config.DubboShutdownHook;
+import org.apache.dubbo.event.EventDispatcher;
+import org.apache.dubbo.event.EventListener;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link DubboShutdownHookRegisteredEvent} Test
+ *
+ * @since 2.7.4
+ */
+public class DubboShutdownHookRegisteredEventTest implements EventListener<DubboShutdownHookRegisteredEvent> {
+
+    private DubboShutdownHookRegisteredEvent event;
+
+    private EventDispatcher eventDispatcher = EventDispatcher.getDefaultExtension();
+
+    private DubboShutdownHook dubboShutdownHook;
+
+    @BeforeEach
+    public void init() {
+        eventDispatcher.removeAllEventListeners();
+        eventDispatcher.addEventListener(this);
+        dubboShutdownHook = DubboShutdownHook.getDubboShutdownHook();
+    }
+
+    @Test
+    public void testOnEvent() {
+        dubboShutdownHook.register();
+        assertEquals(dubboShutdownHook, event.getSource());
+        assertEquals(dubboShutdownHook, event.getDubboShutdownHook());
+    }
+
+    @Override
+    public void onEvent(DubboShutdownHookRegisteredEvent event) {
+        this.event = event;
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/DubboShutdownHookUnregisteredEventTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/DubboShutdownHookUnregisteredEventTest.java
new file mode 100644
index 0000000..d4acfed
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/DubboShutdownHookUnregisteredEventTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.event;
+
+import org.apache.dubbo.config.DubboShutdownHook;
+import org.apache.dubbo.event.EventDispatcher;
+import org.apache.dubbo.event.EventListener;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link DubboShutdownHookUnregisteredEvent} Test
+ *
+ * @since 2.7.4
+ */
+public class DubboShutdownHookUnregisteredEventTest implements EventListener<DubboShutdownHookUnregisteredEvent> {
+
+    private DubboShutdownHookUnregisteredEvent event;
+
+    private EventDispatcher eventDispatcher = EventDispatcher.getDefaultExtension();
+
+    private DubboShutdownHook dubboShutdownHook;
+
+    @BeforeEach
+    public void init() {
+        eventDispatcher.removeAllEventListeners();
+        eventDispatcher.addEventListener(this);
+        dubboShutdownHook = DubboShutdownHook.getDubboShutdownHook();
+    }
+
+    @Test
+    public void testOnEvent() {
+        dubboShutdownHook.register();
+        dubboShutdownHook.unregister();
+        assertEquals(dubboShutdownHook, event.getSource());
+        assertEquals(dubboShutdownHook, event.getDubboShutdownHook());
+    }
+
+    @Override
+    public void onEvent(DubboShutdownHookUnregisteredEvent event) {
+        this.event = event;
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/ReferenceConfigDestroyedEventTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/ReferenceConfigDestroyedEventTest.java
new file mode 100644
index 0000000..3ca1062
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/ReferenceConfigDestroyedEventTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.event;
+
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.event.EventDispatcher;
+import org.apache.dubbo.event.EventListener;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Field;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.springframework.util.ReflectionUtils.findField;
+import static org.springframework.util.ReflectionUtils.setField;
+
+/**
+ * {@link ReferenceConfigDestroyedEvent} Test
+ *
+ * @since 2.7.4
+ */
+public class ReferenceConfigDestroyedEventTest implements EventListener<ReferenceConfigDestroyedEvent> {
+
+    private ReferenceConfigDestroyedEvent event;
+
+    private EventDispatcher eventDispatcher = EventDispatcher.getDefaultExtension();
+
+    private ReferenceConfig referenceConfig;
+
+    @BeforeEach
+    public void init() throws Exception {
+        eventDispatcher.removeAllEventListeners();
+        eventDispatcher.addEventListener(this);
+        referenceConfig = new ReferenceConfig();
+        Field field = findField(referenceConfig.getClass(), "ref");
+        field.setAccessible(true);
+        setField(field, referenceConfig, new Object());
+    }
+
+    @Test
+    public void testOnEvent() {
+        referenceConfig.destroy();
+        assertEquals(referenceConfig, event.getSource());
+    }
+
+    @Override
+    public void onEvent(ReferenceConfigDestroyedEvent event) {
+        this.event = event;
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/ReferenceConfigInitializedEventTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/ReferenceConfigInitializedEventTest.java
new file mode 100644
index 0000000..77bc6b5
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/ReferenceConfigInitializedEventTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.event;
+
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.event.EventDispatcher;
+import org.apache.dubbo.event.EventListener;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link ReferenceConfigInitializedEvent} Test
+ *
+ * @since 2.7.4
+ */
+public class ReferenceConfigInitializedEventTest implements EventListener<ReferenceConfigInitializedEvent> {
+
+    private ReferenceConfigInitializedEvent event;
+
+    private EventDispatcher eventDispatcher = EventDispatcher.getDefaultExtension();
+
+    private ReferenceConfig referenceConfig;
+
+    @BeforeEach
+    public void init() throws Exception {
+        eventDispatcher.removeAllEventListeners();
+        eventDispatcher.addEventListener(this);
+        referenceConfig = new ReferenceConfig();
+    }
+
+    @Test
+    public void testOnEvent() {
+        eventDispatcher.dispatch(new ReferenceConfigInitializedEvent(referenceConfig, null));
+        assertEquals(referenceConfig, event.getSource());
+        assertEquals(referenceConfig, event.getReferenceConfig());
+    }
+
+    @Override
+    public void onEvent(ReferenceConfigInitializedEvent event) {
+        this.event = event;
+    }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporterTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporterTest.java
index 6cf5aba..d66ce0b 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporterTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporterTest.java
@@ -81,7 +81,7 @@ public class ConfigurableMetadataServiceExporterTest {
         assertEquals(MetadataService.class.getName(), url.getServiceInterface());
         assertEquals("test", url.getParameter(GROUP_KEY));
         assertEquals(MetadataService.VERSION, url.getParameter(VERSION_KEY));
-        assertEquals("mockprotocol", url.getProtocol());
+        assertEquals("dubbo", url.getProtocol());
 
         exporter.unexport();
     }
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizerTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizerTest.java
new file mode 100644
index 0000000..1abdca9
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/metadata/ServiceInstancePortCustomizerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.config.metadata;
+
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * {@link ServiceInstancePortCustomizer} Test
+ *
+ * @since 2.7.4
+ */
+public class ServiceInstancePortCustomizerTest {
+
+    private ServiceInstancePortCustomizer customizer;
+
+    private DefaultServiceInstance serviceInstance;
+
+    @BeforeEach
+
+    public void init() {
+        customizer = new ServiceInstancePortCustomizer();
+        serviceInstance = new DefaultServiceInstance();
+        ConfigManager.getInstance()
+                .addProtocol(new ProtocolConfig("rest", 9090));
+    }
+
+    @Test
+    public void testCustomizeWithoutSet() {
+        serviceInstance.setPort(8080);
+        customizer.customize(serviceInstance);
+        assertEquals(8080, serviceInstance.getPort());
+    }
+
+    @Test
+    public void testCustomize() {
+        customizer.customize(serviceInstance);
+        assertEquals(9090, serviceInstance.getPort());
+    }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
index 74ca3f3..1219343 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
@@ -16,7 +16,8 @@
  */
 package org.apache.dubbo.config.spring.context.annotation;
 
-import org.springframework.context.Lifecycle;
+import org.apache.dubbo.common.context.Lifecycle;
+
 import org.springframework.context.annotation.Import;
 
 import java.lang.annotation.Documented;
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycleTest.java
similarity index 52%
copy from dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
copy to dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycleTest.java
index 74ca3f3..70a6daf 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycleTest.java
@@ -16,25 +16,40 @@
  */
 package org.apache.dubbo.config.spring.context.annotation;
 
-import org.springframework.context.Lifecycle;
-import org.springframework.context.annotation.Import;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
- * Enables Dubbo {@link Lifecycle} components
+ * {@link EnableDubboLifecycle} Test
  *
  * @since 2.7.4
  */
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-@Inherited
-@Documented
-@Import(DubboLifecycleComponentRegistrar.class)
-public @interface EnableDubboLifecycle {
+@EnableDubboLifecycle
+public class EnableDubboLifecycleTest {
+
+    private AnnotationConfigApplicationContext context;
+
+    static boolean initialized = false;
+
+    static boolean started = false;
+
+    @BeforeEach
+    public void init() {
+        context = new AnnotationConfigApplicationContext(EnableDubboLifecycleTest.class);
+    }
+
+    @AfterEach
+    public void destroy() {
+        context.close();
+    }
+
+    @Test
+    public void test() {
+        assertTrue(initialized);
+        assertTrue(started);
+    }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/MyLifecycle.java
similarity index 52%
copy from dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
copy to dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/MyLifecycle.java
index 74ca3f3..1efaee8 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboLifecycle.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/MyLifecycle.java
@@ -16,25 +16,43 @@
  */
 package org.apache.dubbo.config.spring.context.annotation;
 
-import org.springframework.context.Lifecycle;
-import org.springframework.context.annotation.Import;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import org.apache.dubbo.common.context.Lifecycle;
 
 /**
- * Enables Dubbo {@link Lifecycle} components
- *
- * @since 2.7.4
+ * {@link Lifecycle}
  */
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-@Inherited
-@Documented
-@Import(DubboLifecycleComponentRegistrar.class)
-public @interface EnableDubboLifecycle {
+public class MyLifecycle implements Lifecycle {
+
+    @Override
+    public Lifecycle initialize() throws IllegalStateException {
+        EnableDubboLifecycleTest.initialized = true;
+        return this;
+    }
+
+    @Override
+    public boolean isInitialized() {
+        return true;
+    }
+
+    @Override
+    public Lifecycle start() throws IllegalStateException {
+        initialize();
+        EnableDubboLifecycleTest.started = true;
+        return this;
+    }
+
+    @Override
+    public boolean isStarted() {
+        return true;
+    }
+
+    @Override
+    public Lifecycle stop() throws IllegalStateException {
+        return this;
+    }
+
+    @Override
+    public void destroy() throws IllegalStateException {
+
+    }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/services/org.apache.dubbo.common.context.Lifecycle b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/services/org.apache.dubbo.common.context.Lifecycle
new file mode 100644
index 0000000..c1e1296
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/services/org.apache.dubbo.common.context.Lifecycle
@@ -0,0 +1 @@
+org.apache.dubbo.config.spring.context.annotation.MyLifecycle
\ No newline at end of file