You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/09/17 06:48:08 UTC

[dubbo] branch 3.0 updated: [3.0] Refactor application/module deploy processing and events (#8824)

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

albumenj pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new afbeaaf  [3.0] Refactor application/module deploy processing and events (#8824)
afbeaaf is described below

commit afbeaafe937c79edfb848803daa488ac9445deb2
Author: Gong Dewei <ky...@qq.com>
AuthorDate: Fri Sep 17 14:47:52 2021 +0800

    [3.0] Refactor application/module deploy processing and events (#8824)
    
    * Init SpringExtensionInjector in DubboInfraBeanRegisterPostProcessor
    
    * Refactor application/module deployer start and listener
    
    * Refactor Dubbo spring initialization and events
    
    * Adjust shutdown hook callback call chain
    
    * Fix license
    
    * Add spring context module reload test
    
    * Fix deadlock of ApplicationModel module while destroy
    
    * Catch error while add/remove java shutdown hook
    
    * Waiting for dubbo start in spring application
    
    * polish deploy log
    
    * Rename DubboSpringInitializaionXxx to DubboSpringInitXxx
    
    * Fix compile error
    
    * fix tests
---
 .../org/apache/dubbo/common/bytecode/Wrapper.java  |   5 +-
 .../dubbo/common/deploy/AbstractDeployer.java      | 133 ++++++++
 .../ApplicationDeployListener.java}                |  15 +-
 .../dubbo/common/deploy/ApplicationDeployer.java   |  34 +-
 .../apache/dubbo/common/deploy/DeployListener.java |  23 +-
 .../dubbo/common/deploy/DeployListenerAdapter.java |  27 +-
 .../apache/dubbo/common/deploy/DeployState.java    |  17 +-
 .../org/apache/dubbo/common/deploy/Deployer.java   |  91 ++++++
 .../dubbo/common/deploy}/ModuleDeployListener.java |  16 +-
 .../apache/dubbo/common/deploy/ModuleDeployer.java |  12 +-
 .../dubbo/common/lang/ShutdownHookCallback.java    |  11 +-
 .../org/apache/dubbo/common/utils/NetUtils.java    |  14 +
 .../apache/dubbo/common/utils/ReflectUtils.java    |  12 +
 .../java/org/apache/dubbo/config/ConfigKeys.java   |   9 -
 .../org/apache/dubbo/config/ServiceConfigBase.java |   8 +-
 .../apache/dubbo/rpc/model/ApplicationModel.java   |  72 ++--
 .../org/apache/dubbo/rpc/model/ModelConstants.java |   4 +
 .../org/apache/dubbo/rpc/model/ModuleModel.java    |  42 +--
 .../org/apache/dubbo/rpc/model/ScopeModel.java     |   4 +
 .../org/apache/dubbo/rpc/model/ServiceModel.java   |   6 +
 .../dubbo/config/ConfigScopeModelInitializer.java  |   5 +-
 .../org/apache/dubbo/config/DubboShutdownHook.java |  62 +---
 .../dubbo/config/bootstrap/DubboBootstrap.java     | 131 ++++++--
 .../bootstrap/DubboBootstrapStartStopListener.java |   1 +
 .../DefaultApplicationDeployer.java                | 361 +++++++++++----------
 .../DefaultModuleDeployer.java                     | 161 ++++++---
 .../ConfigurableMetadataServiceExporter.java       |   4 +-
 .../bootstrap/DubboBootstrapMultiInstanceTest.java |  11 +-
 .../dubbo/config/bootstrap/DubboBootstrapTest.java |  12 +-
 ...RegistryCenterDubboProtocolIntegrationTest.java |  11 +-
 .../metadata/MetadataServiceExporterTest.java      |  40 ++-
 .../apache/dubbo/config/spring/ServiceBean.java    |   8 +-
 .../spring/SpringScopeModelInitializer.java}       |  23 +-
 .../ReferenceAnnotationBeanPostProcessor.java      |   6 +-
 .../context/DubboBootstrapApplicationListener.java |  23 +-
 ...bboBootstrapStartStopListenerSpringAdapter.java |   2 +-
 .../context/DubboConfigApplicationListener.java    |  72 ++++
 .../context/DubboDeployApplicationListener.java    | 126 +++++++
 .../DubboInfraBeanRegisterPostProcessor.java       |   4 +-
 ...ionContext.java => DubboSpringInitContext.java} |  37 ++-
 ...tomizer.java => DubboSpringInitCustomizer.java} |   6 +-
 .../context/DubboSpringInitCustomizerHolder.java   |  58 ++++
 .../spring/context/DubboSpringInitializer.java     |  86 ++---
 .../DubboConfigConfigurationRegistrar.java         |   2 +-
 ...dEvent.java => DubboApplicationStateEvent.java} |  46 +--
 .../context/event/DubboBootstrapStatedEvent.java   |   2 +-
 .../context/event/DubboBootstrapStopedEvent.java   |   2 +-
 ...nInitedEvent.java => DubboConfigInitEvent.java} |   9 +-
 .../spring/reference/ReferenceBeanManager.java     |  11 +-
 .../dubbo/config/spring/util/DubboBeanUtils.java   |  32 +-
 ...rg.apache.dubbo.rpc.model.ScopeModelInitializer |   1 +
 .../org/apache/dubbo/config/spring/ConfigTest.java |  10 +-
 .../dubbo/config/spring/ShutdownHookTest.java      |  15 +-
 .../annotation/MethodConfigCallbackTest.java       |   4 +-
 .../customize/DubboSpringInitCustomizerTest.java   | 114 +++++++
 .../spring/context/customize/dubbo-provider-v1.xml |  36 ++
 .../spring/context/customize/dubbo-provider-v2.xml |  36 ++
 .../extension/SpringExtensionInjectorTest.java     |  12 +-
 .../reference/DubboConfigBeanInitializerTest.java  |  13 +-
 .../spring/registrycenter/RegistryCenter.java      |   5 +
 .../registrycenter/ZookeeperRegistryCenter.java    |   5 +
 .../metadata/report/MetadataReportInstance.java    |   4 -
 .../dubbo/qos/command/impl/ChangeTelnet.java       |   5 +-
 .../apache/dubbo/qos/command/impl/CountTelnet.java |   5 +-
 .../dubbo/qos/command/impl/InvokeTelnet.java       |   7 +-
 .../dubbo/qos/command/impl/PublishMetadata.java    |   2 +-
 .../dubbo/qos/command/impl/SelectTelnet.java       |   5 +-
 .../dubbo/qos/command/impl/ShutdownTelnet.java     |   2 +-
 ...inessProbe.java => DeployerReadinessProbe.java} |  21 +-
 ...StartupProbe.java => DeployerStartupProbe.java} |  18 +-
 .../qos/probe/impl/ProviderReadinessProbe.java     |   2 +
 .../java/org/apache/dubbo/qos/server/Server.java   |  11 +-
 .../qos/server/handler/HttpProcessHandler.java     |  17 +-
 .../qos/server/handler/QosProcessHandler.java      |   5 +-
 .../qos/server/handler/TelnetProcessHandler.java   |   7 +-
 .../org.apache.dubbo.qos.probe.ReadinessProbe      |   4 +-
 .../org.apache.dubbo.qos.probe.StartupProbe        |   2 +-
 .../qos/command/DefaultCommandExecutorTest.java    |   1 -
 .../dubbo/qos/command/impl/ChangeTelnetTest.java   |   5 +-
 .../dubbo/qos/command/impl/CountTelnetTest.java    |   1 -
 .../apache/dubbo/qos/command/impl/HelpTest.java    |   1 -
 .../dubbo/qos/command/impl/InvokeTelnetTest.java   |   5 +-
 .../dubbo/qos/command/impl/PortTelnetTest.java     |   1 -
 .../dubbo/qos/command/impl/SelectTelnetTest.java   |   5 +-
 .../dubbo/qos/command/impl/ShutdownTelnetTest.java |   3 +-
 .../dubbo/qos/command/util/CommandHelperTest.java  |   1 -
 .../dubbo/qos/protocol/QosProtocolWrapperTest.java |   1 -
 .../qos/server/handler/HttpProcessHandlerTest.java |   3 +-
 .../qos/server/handler/QosProcessHandlerTest.java  |   3 +-
 .../server/handler/TelnetProcessHandlerTest.java   |   3 +-
 .../metadata/ServiceInstanceMetadataUtils.java     |   3 +-
 .../support/AbstractRegistryFactoryTest.java       |   1 -
 .../registry/zookeeper/ZookeeperRegistryTest.java  |   3 +-
 .../dubbo/rpc/protocol/dubbo/DubboProtocol.java    |   4 +-
 .../rpc/protocol/dubbo/ArgumentCallbackTest.java   |   1 -
 .../rpc/protocol/tri/AbstractClientStream.java     |   5 +-
 .../rpc/protocol/tri/AbstractServerStream.java     |   3 +-
 .../dubbo/rpc/protocol/tri/AbstractStream.java     |   9 +-
 .../apache/dubbo/rpc/protocol/tri/GrpcStatus.java  |   5 +-
 .../rpc/protocol/tri/TripleClientHandler.java      |  13 +-
 .../tri/TripleHttp2FrameServerHandler.java         |  27 +-
 .../rpc/protocol/tri/TripleHttp2Protocol.java      |  15 +-
 .../dubbo/rpc/protocol/tri/TripleInvoker.java      |   3 +-
 .../dubbo/rpc/protocol/tri/UnaryClientStream.java  |   7 +-
 .../dubbo/rpc/protocol/tri/UnaryServerStream.java  |   3 +-
 .../endpoint/metadata/DubboShutdownMetadata.java   |   1 -
 .../event/AwaitingNonWebApplicationListener.java   |   8 +-
 .../test/spring/SpringAnnotationBeanTest.java      |   6 +-
 .../test/spring/SpringJavaConfigBeanTest.java      |   4 +-
 .../dubbo/test/spring/SpringXmlConfigTest.java     |   4 +-
 ...stomizer.java => MockSpringInitCustomizer.java} |  28 +-
 ...config.spring.context.DubboSpringInitCustomizer |   1 +
 ...ing.context.DubboSpringInitializationCustomizer |   1 -
 113 files changed, 1662 insertions(+), 803 deletions(-)

diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java
index 6a3c4a1..6f0421a 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Wrapper.java
@@ -16,11 +16,10 @@
  */
 package org.apache.dubbo.common.bytecode;
 
-import org.apache.dubbo.common.utils.ClassUtils;
-import org.apache.dubbo.common.utils.ReflectUtils;
-
 import javassist.ClassPool;
 import javassist.CtMethod;
+import org.apache.dubbo.common.utils.ClassUtils;
+import org.apache.dubbo.common.utils.ReflectUtils;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/AbstractDeployer.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/AbstractDeployer.java
new file mode 100644
index 0000000..05448d2
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/AbstractDeployer.java
@@ -0,0 +1,133 @@
+/*
+ * 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.deploy;
+
+import org.apache.dubbo.rpc.model.ScopeModel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.apache.dubbo.common.deploy.DeployState.FAILED;
+import static org.apache.dubbo.common.deploy.DeployState.PENDING;
+import static org.apache.dubbo.common.deploy.DeployState.STARTED;
+import static org.apache.dubbo.common.deploy.DeployState.STARTING;
+import static org.apache.dubbo.common.deploy.DeployState.STOPPED;
+import static org.apache.dubbo.common.deploy.DeployState.STOPPING;
+
+public abstract class AbstractDeployer<E extends ScopeModel> implements Deployer<E> {
+
+    private volatile DeployState state = PENDING;
+
+    protected AtomicBoolean initialized = new AtomicBoolean(false);
+
+    private List<DeployListener<E>> listeners = new ArrayList<>();
+
+    private E scopeModel;
+
+    public AbstractDeployer(E scopeModel) {
+        this.scopeModel = scopeModel;
+    }
+
+    @Override
+    public boolean isPending() {
+        return state == PENDING;
+    }
+
+    @Override
+    public boolean isRunning() {
+        return state == STARTING || state == STARTED;
+    }
+
+    @Override
+    public boolean isStarted() {
+        return state == STARTED;
+    }
+
+    @Override
+    public boolean isStarting() {
+        return state == STARTING;
+    }
+
+    @Override
+    public boolean isStopping() {
+        return state == STOPPING;
+    }
+
+    @Override
+    public boolean isStopped() {
+        return state == STOPPED;
+    }
+
+    @Override
+    public boolean isFailed() {
+        return state == FAILED;
+    }
+
+    @Override
+    public DeployState getState() {
+        return state;
+    }
+
+    @Override
+    public void addDeployListener(DeployListener<E> listener) {
+        listeners.add(listener);
+    }
+
+    @Override
+    public void removeDeployListener(DeployListener<E> listener) {
+        listeners.remove(listener);
+    }
+
+    protected void setStarting() {
+        this.state = STARTING;
+        for (DeployListener<E> listener : listeners) {
+            listener.onStarting(scopeModel);
+        }
+    }
+
+    protected void setStarted() {
+        this.state = STARTED;
+        for (DeployListener<E> listener : listeners) {
+            listener.onStarted(scopeModel);
+        }
+    }
+    protected void setStopping() {
+        this.state = STOPPING;
+        for (DeployListener<E> listener : listeners) {
+            listener.onStopping(scopeModel);
+        }
+    }
+
+    protected void setStopped() {
+        this.state = STOPPED;
+        for (DeployListener<E> listener : listeners) {
+            listener.onStopped(scopeModel);
+        }
+    }
+
+    protected void setFailed(Throwable cause) {
+        this.state = FAILED;
+        for (DeployListener<E> listener : listeners) {
+            listener.onFailure(scopeModel, cause);
+        }
+    }
+
+    public boolean isInitialized() {
+        return initialized.get();
+    }
+}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallback.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployListener.java
similarity index 76%
copy from dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallback.java
copy to dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployListener.java
index 3ce6556..17fc82c 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallback.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployListener.java
@@ -14,23 +14,16 @@
  * 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.deploy;
 
 import org.apache.dubbo.common.extension.ExtensionScope;
 import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 
 /**
- * Dubbo ShutdownHook callback interface
- *
- * @since 2.7.5
+ * Listen for Dubbo application deployment events
  */
 @SPI(scope = ExtensionScope.APPLICATION)
-public interface ShutdownHookCallback extends Prioritized {
+public interface ApplicationDeployListener extends DeployListener<ApplicationModel> {
 
-    /**
-     * Callback execution
-     *
-     * @throws Throwable if met with some errors
-     */
-    void callback() throws Throwable;
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployer.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployer.java
index 7c2a04a..471a863 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployer.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ApplicationDeployer.java
@@ -17,32 +17,48 @@
 package org.apache.dubbo.common.deploy;
 
 import org.apache.dubbo.common.config.ReferenceCache;
-import org.apache.dubbo.common.context.Lifecycle;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
+import java.util.concurrent.CompletableFuture;
+
 /**
  * initialize and start application instance
  */
-public interface ApplicationDeployer extends Lifecycle {
+public interface ApplicationDeployer extends Deployer<ApplicationModel> {
+
+    /**
+     * Initialize the component
+     */
+    void initialize() throws IllegalStateException;
 
-    void initialize();
+    /**
+     * Starts the component.
+     */
+    CompletableFuture start() throws IllegalStateException;
 
-    void start();
+    /**
+     * Stops the component.
+     */
+    void stop() throws IllegalStateException;
 
     void prepareApplicationInstance();
 
     void destroy();
 
+    /**
+     * Indicates that the Application is initialized or not.
+     * @return
+     */
     boolean isInitialized();
 
-    boolean isStarted();
+    ApplicationModel getApplicationModel();
 
-    boolean isStartup();
+    ReferenceCache getReferenceCache();
 
-    boolean isShutdown();
+    boolean isAsync();
 
-    ApplicationModel getApplicationModel();
+    void checkStarting();
 
-    ReferenceCache getReferenceCache();
+    void checkStarted();
 
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListener.java
similarity index 68%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java
copy to dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListener.java
index 74105e7..fb0e7ad 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListener.java
@@ -14,17 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.probe.impl;
+package org.apache.dubbo.common.deploy;
 
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.probe.StartupProbe;
+import org.apache.dubbo.rpc.model.ScopeModel;
 
-@Activate
-public class BootstrapStartupProbe implements StartupProbe {
+public interface DeployListener<E extends ScopeModel> {
+
+    void onStarting(E scopeModel);
+
+    void onStarted(E scopeModel);
+
+    void onStopping(E scopeModel);
+
+    void onStopped(E scopeModel);
+
+    void onFailure(E scopeModel, Throwable cause);
 
-    @Override
-    public boolean check() {
-        return DubboBootstrap.getInstance().isStartup();
-    }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListenerAdapter.java
similarity index 63%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java
copy to dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListenerAdapter.java
index 74105e7..ed78f40 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployListenerAdapter.java
@@ -14,17 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.probe.impl;
+package org.apache.dubbo.common.deploy;
 
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.probe.StartupProbe;
+import org.apache.dubbo.rpc.model.ScopeModel;
 
-@Activate
-public class BootstrapStartupProbe implements StartupProbe {
+public class DeployListenerAdapter<E extends ScopeModel> implements DeployListener<E>{
+    @Override
+    public void onStarting(E scopeModel) {
+    }
+
+    @Override
+    public void onStarted(E scopeModel) {
+    }
+
+    @Override
+    public void onStopping(E scopeModel) {
+    }
+
+    @Override
+    public void onStopped(E scopeModel) {
+    }
 
     @Override
-    public boolean check() {
-        return DubboBootstrap.getInstance().isStartup();
+    public void onFailure(E scopeModel, Throwable cause) {
     }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapReadinessProbe.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployState.java
similarity index 65%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapReadinessProbe.java
copy to dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployState.java
index edf8f60..eda92c4 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapReadinessProbe.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/DeployState.java
@@ -14,16 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.qos.probe.impl;
+package org.apache.dubbo.common.deploy;
 
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.probe.ReadinessProbe;
-
-@Activate
-public class BootstrapReadinessProbe implements ReadinessProbe {
-    @Override
-    public boolean check() {
-        return !DubboBootstrap.getInstance().isShutdown() && DubboBootstrap.getInstance().isStartup();
-    }
+/**
+ * Deploy state enum
+ */
+public enum DeployState {
+    PENDING, STARTING, STARTED, STOPPING, STOPPED, FAILED
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/Deployer.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/Deployer.java
new file mode 100644
index 0000000..b4d0704
--- /dev/null
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/Deployer.java
@@ -0,0 +1,91 @@
+/*
+ * 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.deploy;
+
+import org.apache.dubbo.rpc.model.ScopeModel;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ */
+public interface Deployer<E extends ScopeModel> {
+
+    /**
+     * Initialize the component
+     */
+    void initialize() throws IllegalStateException;
+
+    /**
+     * Starts the component.
+     */
+    CompletableFuture start() throws IllegalStateException;
+
+    /**
+     * Stops the component.
+     */
+    void stop() throws IllegalStateException;
+
+    /**
+     * @return true if the component is added and waiting to start
+     */
+    boolean isPending();
+
+    /**
+     * @return true if the component is starting or has been started.
+     */
+    boolean isRunning();
+
+    /**
+     * @return true if the component has been started.
+     * @see #start()
+     * @see #isStarting()
+     */
+    boolean isStarted();
+
+    /**
+     * @return true if the component is starting.
+     * @see #isStarted()
+     */
+    boolean isStarting();
+
+    /**
+     * @return true if the component is stopping.
+     * @see #isStopped()
+     */
+    boolean isStopping();
+
+    /**
+     * @return true if the component is stopping.
+     * @see #isStopped()
+     */
+    boolean isStopped();
+
+    /**
+     * @return true if the component has failed to start or has failed to stop.
+     */
+    boolean isFailed();
+
+    /**
+     * @return current state
+     */
+    DeployState getState();
+
+    void addDeployListener(DeployListener<E> listener);
+
+    void removeDeployListener(DeployListener<E> listener);
+
+}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/ModuleDeployListener.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ModuleDeployListener.java
similarity index 73%
rename from dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/ModuleDeployListener.java
rename to dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ModuleDeployListener.java
index 18cc9e3..bf76984 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/ModuleDeployListener.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ModuleDeployListener.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config.bootstrap;
+package org.apache.dubbo.common.deploy;
 
 import org.apache.dubbo.common.extension.ExtensionScope;
 import org.apache.dubbo.common.extension.SPI;
@@ -24,18 +24,6 @@ import org.apache.dubbo.rpc.model.ModuleModel;
  * Module deploy listener
  */
 @SPI(scope = ExtensionScope.MODULE)
-public interface ModuleDeployListener {
-
-    /**
-     * Notify after module is started (export/refer services)
-     * @param moduleModel
-     */
-    void onModuleStarted(ModuleModel moduleModel);
-
-    /**
-     * Notify after module is stopped
-     * @param moduleModel
-     */
-    void onModuleStopped(ModuleModel moduleModel);
+public interface ModuleDeployListener extends DeployListener<ModuleModel> {
 
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ModuleDeployer.java b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ModuleDeployer.java
index 16f0161..c741d94 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ModuleDeployer.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/deploy/ModuleDeployer.java
@@ -18,32 +18,30 @@ package org.apache.dubbo.common.deploy;
 
 import org.apache.dubbo.common.config.ReferenceCache;
 import org.apache.dubbo.config.ServiceConfigBase;
+import org.apache.dubbo.rpc.model.ModuleModel;
 
 import java.util.concurrent.CompletableFuture;
 
 /**
  * Export/refer services of module
  */
-public interface ModuleDeployer {
+public interface ModuleDeployer extends Deployer<ModuleModel> {
 
     void initialize() throws IllegalStateException;
 
     CompletableFuture start() throws IllegalStateException;
 
-    void destroy() throws IllegalStateException;
+    void stop() throws IllegalStateException;
 
-    boolean isStartup();
+    void destroy() throws IllegalStateException;
 
     boolean isInitialized();
 
-    boolean isExportBackground();
-
-    boolean isReferBackground();
-
     ReferenceCache getReferenceCache();
 
     void prepare();
 
     void notifyExportService(ServiceConfigBase<?> sc);
 
+    boolean isAsync();
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallback.java b/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallback.java
index 3ce6556..c73e3d4 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallback.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/lang/ShutdownHookCallback.java
@@ -20,9 +20,18 @@ import org.apache.dubbo.common.extension.ExtensionScope;
 import org.apache.dubbo.common.extension.SPI;
 
 /**
- * Dubbo ShutdownHook callback interface
+ * A callback interface invoked when Dubbo application is stopped.
+ * <p>Note: This class is not directly related to Java ShutdownHook.</p>
+ * <p/>
+ * <p>Call chains:</p>
+ * <ol>
+ *     <li>Java Shutdown Hook -> ApplicationDeployer.destroy() -> execute ShutdownHookCallback</li>
+ *     <li>Stop dubbo application -> ApplicationDeployer.destroy() -> execute ShutdownHookCallback</li>
+ * </ol>
  *
  * @since 2.7.5
+ * @see org.apache.dubbo.common.deploy.ApplicationDeployListener
+ * @see org.apache.dubbo.rpc.model.ScopeModelDestroyListener
  */
 @SPI(scope = ExtensionScope.APPLICATION)
 public interface ShutdownHookCallback extends Prioritized {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
index 00cc011..96ce1f0 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/NetUtils.java
@@ -119,6 +119,20 @@ public class NetUtils {
         return port;
     }
 
+    /**
+     * Check the port whether is in use in os
+     * @param port
+     * @return
+     */
+    public static boolean isPortInUsed(int port) {
+        try (ServerSocket ignored = new ServerSocket(port)) {
+            return false;
+        } catch (IOException e) {
+            // continue
+        }
+        return true;
+    }
+
     public static boolean isInvalidPort(int port) {
         return port < MIN_PORT || port > MAX_PORT;
     }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java
index 47688c2..af3c627 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java
@@ -1368,4 +1368,16 @@ public final class ReflectUtils {
         return fieldNames;
     }
 
+    public static <T> T getFieldValue(Object obj, String fieldName) throws RuntimeException {
+        if (obj == null) {
+            throw new IllegalArgumentException("object is null");
+        }
+        try {
+            Field field = obj.getClass().getDeclaredField(fieldName);
+            field.setAccessible(true);
+            return (T) field.get(obj);
+        } catch (Exception e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigKeys.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigKeys.java
index e7089b4..4e3297b 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigKeys.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ConfigKeys.java
@@ -48,13 +48,4 @@ public interface ConfigKeys {
      */
     String DUBBO_CONFIG_IGNORE_DUPLICATED_INTERFACE = "dubbo.config.ignore-duplicated-interface";
 
-    /**
-     * Disable dubbo shutdown hook, Default value is false.
-     * When disable the shutdown hook, you may need to do :
-     * 1. offline all services by qos manually before shutdown dubbo application
-     * 2. no DubboBootstrapStopedEvent will be sent
-     * 3. all ShutdownHookCallback will not be called
-     * 4. dubbo application will not be destroyed when spring context is closed
-     */
-    String DUBBO_LIFECYCLE_DISABLE_SHUTDOWN_HOOK = "dubbo.lifecycle.disable-shutdown-hook";
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java
index 8694be3..15fec05 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ServiceConfigBase.java
@@ -185,11 +185,15 @@ public abstract class ServiceConfigBase<T> extends AbstractServiceConfig {
         }
         if (!interfaceClass.isInstance(ref)) {
             throw new IllegalStateException("The class "
-                    + ref.getClass().getName() + " unimplemented interface "
-                    + interfaceClass + "!");
+                + ref.getClass().getName() + getClassloaderDesc(ref.getClass()) + " unimplemented interface "
+                + interfaceClass + getClassloaderDesc(ref.getClass()) + "!");
         }
     }
 
+    private String getClassloaderDesc(Class clazz) {
+        return "[classloader=" + clazz.getClassLoader().getClass().getName() + "@" + clazz.getClassLoader().hashCode() + "]";
+    }
+
     public Optional<String> getContextPath(ProtocolConfig protocolConfig) {
         String contextPath = protocolConfig.getContextpath();
         if (StringUtils.isEmpty(contextPath) && provider != null) {
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
index fab944b..7d14388 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java
@@ -59,6 +59,7 @@ public class ApplicationModel extends ScopeModel {
     private Environment environment;
     private ConfigManager configManager;
     private ServiceRepository serviceRepository;
+    private ApplicationDeployer deployer;
 
     private final FrameworkModel frameworkModel;
 
@@ -68,6 +69,7 @@ public class ApplicationModel extends ScopeModel {
 
     // internal module index is 0, default module index is 1
     private AtomicInteger moduleIndex = new AtomicInteger(0);
+    private Object moduleLock = new Object();
 
 
     // --------- static methods ----------//
@@ -163,6 +165,10 @@ public class ApplicationModel extends ScopeModel {
         this.frameworkModel = frameworkModel;
         frameworkModel.addApplication(this);
         initialize();
+        // bind to default instance if absent
+        if (defaultInstance == null) {
+            defaultInstance = this;
+        }
     }
 
     @Override
@@ -208,6 +214,11 @@ public class ApplicationModel extends ScopeModel {
             frameworkModel.removeApplication(this);
         }
 
+        if (deployer != null) {
+            deployer.destroy();
+            deployer = null;
+        }
+
         notifyDestroy();
 
         if (environment != null) {
@@ -227,7 +238,7 @@ public class ApplicationModel extends ScopeModel {
     public FrameworkModel getFrameworkModel() {
         return frameworkModel;
     }
-    public synchronized ModuleModel newModule() {
+    public ModuleModel newModule() {
         return new ModuleModel(this);
     }
 
@@ -264,27 +275,31 @@ public class ApplicationModel extends ScopeModel {
         return getCurrentConfig().getName();
     }
 
-    public synchronized void addModule(ModuleModel moduleModel, boolean isInternal) {
-        if (!this.moduleModels.contains(moduleModel)) {
-            this.moduleModels.add(moduleModel);
-            moduleModel.setInternalName(buildInternalName(ModuleModel.NAME, getInternalId(), moduleIndex.getAndIncrement()));
-            if (!isInternal) {
-                pubModuleModels.add(moduleModel);
+    void addModule(ModuleModel moduleModel, boolean isInternal) {
+        synchronized (moduleLock) {
+            if (!this.moduleModels.contains(moduleModel)) {
+                this.moduleModels.add(moduleModel);
+                moduleModel.setInternalName(buildInternalName(ModuleModel.NAME, getInternalId(), moduleIndex.getAndIncrement()));
+                if (!isInternal) {
+                    pubModuleModels.add(moduleModel);
+                }
             }
         }
     }
 
-    public synchronized void removeModule(ModuleModel moduleModel) {
-        this.moduleModels.remove(moduleModel);
-        this.pubModuleModels.remove(moduleModel);
-        if (moduleModel == defaultModule) {
-            defaultModule = findDefaultModule();
-        }
-        if (this.moduleModels.size() == 1 && this.moduleModels.get(0) == internalModule) {
-            this.internalModule.destroy();
-        }
-        if (this.moduleModels.isEmpty()) {
-            destroy();
+    public void removeModule(ModuleModel moduleModel) {
+        synchronized (moduleLock) {
+            this.moduleModels.remove(moduleModel);
+            this.pubModuleModels.remove(moduleModel);
+            if (moduleModel == defaultModule) {
+                defaultModule = findDefaultModule();
+            }
+            if (this.moduleModels.size() == 1 && this.moduleModels.get(0) == internalModule) {
+                this.internalModule.destroy();
+            }
+            if (this.moduleModels.isEmpty()) {
+                destroy();
+            }
         }
     }
 
@@ -293,14 +308,21 @@ public class ApplicationModel extends ScopeModel {
     }
 
     public List<ModuleModel> getPubModuleModels() {
-        return pubModuleModels;
+        return Collections.unmodifiableList(pubModuleModels);
     }
 
-    public synchronized ModuleModel getDefaultModule() {
+    public ModuleModel getDefaultModule() {
         if (defaultModule == null) {
-            defaultModule = findDefaultModule();
-            if (defaultModule == null) {
-                defaultModule = this.newModule();
+            if (isDestroyed()) {
+                return null;
+            }
+            synchronized (moduleLock) {
+                if (defaultModule == null) {
+                    defaultModule = findDefaultModule();
+                    if (defaultModule == null) {
+                        defaultModule = this.newModule();
+                    }
+                }
             }
         }
         return defaultModule;
@@ -360,10 +382,10 @@ public class ApplicationModel extends ScopeModel {
     }
 
     public ApplicationDeployer getDeployer() {
-        return getAttribute(ModelConstants.DEPLOYER, ApplicationDeployer.class);
+        return deployer;
     }
 
     public void setDeployer(ApplicationDeployer deployer) {
-        setAttribute(ModelConstants.DEPLOYER, deployer);
+        this.deployer = deployer;
     }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelConstants.java
index d226d6d..447c798 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModelConstants.java
@@ -20,4 +20,8 @@ public interface ModelConstants {
 
     String DEPLOYER = "deployer";
 
+    /**
+     * Keep Dubbo running when spring is stopped
+     */
+    String KEEP_RUNNING_ON_SPRING_CLOSED = "keepRunningOnSpringClosed";
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java
index 15c4d37..90098ac 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ModuleModel.java
@@ -26,7 +26,6 @@ import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.Assert;
 import org.apache.dubbo.config.context.ModuleConfigManager;
 
-import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
@@ -43,6 +42,7 @@ public class ModuleModel extends ScopeModel {
     private ModuleEnvironment moduleEnvironment;
     private ModuleServiceRepository serviceRepository;
     private ModuleConfigManager moduleConfigManager;
+    private ModuleDeployer deployer;
 
     public ModuleModel(ApplicationModel applicationModel) {
         this(applicationModel, false);
@@ -82,45 +82,23 @@ public class ModuleModel extends ScopeModel {
 
     @Override
     public void onDestroy() {
-        if (serviceRepository != null) {
-            List<ConsumerModel> consumerModels = serviceRepository.getReferredServices();
-
-            for (ConsumerModel consumerModel : consumerModels) {
-                try {
-                    if (consumerModel.getReferenceConfig() != null) {
-                        consumerModel.getReferenceConfig().destroy();
-                    } else if (consumerModel.getDestroyCaller() != null) {
-                        consumerModel.getDestroyCaller().call();
-                    }
-                } catch (Throwable t) {
-                    logger.error("Unable to destroy consumerModel.", t);
-                }
-            }
-
-            List<ProviderModel> exportedServices = serviceRepository.getExportedServices();
-            for (ProviderModel providerModel : exportedServices) {
-                try {
-                    if (providerModel.getServiceConfig() != null) {
-                        providerModel.getServiceConfig().unexport();
-                    } else if (providerModel.getDestroyCaller() != null) {
-                        providerModel.getDestroyCaller().call();
-                    }
-                } catch (Throwable t) {
-                    logger.error("Unable to destroy providerModel.", t);
-                }
-            }
+        applicationModel.removeModule(this);
 
+        if (deployer != null) {
+            deployer.destroy();
+            deployer = null;
+        }
+        if (serviceRepository != null) {
             serviceRepository.destroy();
             serviceRepository = null;
         }
 
         notifyDestroy();
+
         if (moduleEnvironment != null) {
             moduleEnvironment.destroy();
             moduleEnvironment = null;
         }
-
-        applicationModel.removeModule(this);
     }
 
     public ApplicationModel getApplicationModel() {
@@ -153,11 +131,11 @@ public class ModuleModel extends ScopeModel {
     }
 
     public ModuleDeployer getDeployer() {
-        return getAttribute(ModelConstants.DEPLOYER, ModuleDeployer.class);
+        return deployer;
     }
 
     public void setDeployer(ModuleDeployer deployer) {
-        setAttribute(ModelConstants.DEPLOYER, deployer);
+        this.deployer = deployer;
     }
 
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModel.java
index 6b3c22c..a7bc5c5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModel.java
@@ -113,6 +113,10 @@ public abstract class ScopeModel implements ExtensionAccessor {
         }
     }
 
+    public boolean isDestroyed() {
+        return destroyed.get();
+    }
+
     protected void notifyDestroy() {
         for (ScopeModelDestroyListener destroyListener : destroyListeners) {
             destroyListener.onDestroy(this);
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceModel.java b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceModel.java
index 1945862..23bb654 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ServiceModel.java
@@ -104,6 +104,9 @@ public class ServiceModel {
     }
 
     public ReferenceConfigBase<?> getReferenceConfig() {
+        if (config == null) {
+            return null;
+        }
         if (config instanceof ReferenceConfigBase) {
             return (ReferenceConfigBase<?>) config;
         } else {
@@ -112,6 +115,9 @@ public class ServiceModel {
     }
 
     public ServiceConfigBase<?> getServiceConfig() {
+        if (config == null) {
+            return null;
+        }
         if (config instanceof ServiceConfigBase) {
             return (ServiceConfigBase<?>) config;
         } else {
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigScopeModelInitializer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigScopeModelInitializer.java
index 994116d..8639cc2 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigScopeModelInitializer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigScopeModelInitializer.java
@@ -19,8 +19,8 @@ package org.apache.dubbo.config;
 import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
 import org.apache.dubbo.common.deploy.ApplicationDeployer;
 import org.apache.dubbo.common.deploy.ModuleDeployer;
-import org.apache.dubbo.config.bootstrap.DefaultApplicationDeployer;
-import org.apache.dubbo.config.bootstrap.DefaultModuleDeployer;
+import org.apache.dubbo.config.deploy.DefaultApplicationDeployer;
+import org.apache.dubbo.config.deploy.DefaultModuleDeployer;
 import org.apache.dubbo.config.utils.DefaultConfigValidator;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
@@ -37,7 +37,6 @@ public class ConfigScopeModelInitializer implements ScopeModelInitializer {
     @Override
     public void initializeApplicationModel(ApplicationModel applicationModel) {
         ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
-        beanFactory.registerBean(DubboShutdownHook.class);
         beanFactory.registerBean(DefaultConfigValidator.class);
         // applicationDeployer
         ApplicationDeployer applicationDeployer = beanFactory.registerBean(DefaultApplicationDeployer.class);
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
index 45daa42..c8beaf0 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java
@@ -16,14 +16,11 @@
  */
 package org.apache.dubbo.config;
 
-import org.apache.dubbo.common.lang.ShutdownHookCallback;
-import org.apache.dubbo.common.lang.ShutdownHookCallbacks;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.Assert;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
-import java.util.Collection;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -36,7 +33,7 @@ public class DubboShutdownHook extends Thread {
 
     private static final Logger logger = LoggerFactory.getLogger(DubboShutdownHook.class);
 
-    private final ShutdownHookCallbacks callbacks;
+    private final ApplicationModel applicationModel;
 
     /**
      * Has it already been registered or not?
@@ -50,52 +47,24 @@ public class DubboShutdownHook extends Thread {
 
     public DubboShutdownHook(ApplicationModel applicationModel) {
         super("DubboShutdownHook");
-        this.callbacks = applicationModel.getBeanFactory().getBean(ShutdownHookCallbacks.class);
-        Assert.notNull(this.callbacks, "ShutdownHookCallbacks is null");
+        this.applicationModel = applicationModel;
+        Assert.notNull(this.applicationModel, "ApplicationModel is null");
     }
 
-//    public static DubboShutdownHook getDubboShutdownHook() {
-//        return DUBBO_SHUTDOWN_HOOK;
-//    }
-
     @Override
     public void run() {
-        String disableShutdownHookValue = System.getProperty(ConfigKeys.DUBBO_LIFECYCLE_DISABLE_SHUTDOWN_HOOK, "false");
-        if (Boolean.parseBoolean(disableShutdownHookValue)) {
-            if (logger.isWarnEnabled()) {
-                logger.warn("Shutdown hook is disabled, please shutdown dubbo services by qos manually");
-            }
-            return;
-        }
 
         if (destroyed.compareAndSet(false, true)) {
             if (logger.isInfoEnabled()) {
                 logger.info("Run shutdown hook now.");
             }
 
-            callback();
             doDestroy();
         }
     }
 
-    /**
-     * For testing purpose
-     */
-    void clear() {
-        callbacks.clear();
-    }
-
-    private void callback() {
-        callbacks.callback();
-    }
-
-    public DubboShutdownHook addCallback(ShutdownHookCallback callback) {
-        callbacks.addCallback(callback);
-        return this;
-    }
-
-    public Collection<ShutdownHookCallback> getCallbacks() {
-        return callbacks.getCallbacks();
+    private void doDestroy() {
+        applicationModel.destroy();
     }
 
     /**
@@ -103,7 +72,11 @@ public class DubboShutdownHook extends Thread {
      */
     public void register() {
         if (registered.compareAndSet(false, true)) {
-            Runtime.getRuntime().addShutdownHook(this);
+            try {
+                Runtime.getRuntime().addShutdownHook(this);
+            } catch (Exception e) {
+                logger.warn("register shutdown hook failed: " + e.getMessage(), e);
+            }
         }
     }
 
@@ -112,16 +85,11 @@ public class DubboShutdownHook extends Thread {
      */
     public void unregister() {
         if (registered.compareAndSet(true, false)) {
-            Runtime.getRuntime().removeShutdownHook(this);
-        }
-    }
-
-    /**
-     * Destroy all the resources, including registries and protocols.
-     */
-    public void doDestroy() {
-        if (logger.isInfoEnabled()) {
-            logger.info("Dubbo Service has been destroyed.");
+            try {
+                Runtime.getRuntime().removeShutdownHook(this);
+            } catch (Exception e) {
+                logger.warn("unregister shutdown hook failed: " + e.getMessage(), e);
+            }
         }
     }
 
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index e112594..2fb0ccf 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.config.bootstrap;
 import org.apache.dubbo.common.config.Environment;
 import org.apache.dubbo.common.config.ReferenceCache;
 import org.apache.dubbo.common.deploy.ApplicationDeployer;
+import org.apache.dubbo.common.deploy.DeployListenerAdapter;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
@@ -45,14 +46,16 @@ import org.apache.dubbo.config.bootstrap.builders.ReferenceBuilder;
 import org.apache.dubbo.config.bootstrap.builders.RegistryBuilder;
 import org.apache.dubbo.config.bootstrap.builders.ServiceBuilder;
 import org.apache.dubbo.config.context.ConfigManager;
-import org.apache.dubbo.config.utils.CompositeReferenceCache;
-import org.apache.dubbo.metadata.report.MetadataReportInstance;
+import org.apache.dubbo.metadata.report.support.AbstractMetadataReportFactory;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.model.ModuleModel;
 
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
@@ -77,6 +80,7 @@ public final class DubboBootstrap {
 
     private static final Logger logger = LoggerFactory.getLogger(DubboBootstrap.class);
 
+    private static volatile Map<ApplicationModel, DubboBootstrap> instanceMap = new ConcurrentHashMap<>();
     private static volatile DubboBootstrap instance;
 
     private final AtomicBoolean awaited = new AtomicBoolean(false);
@@ -95,8 +99,6 @@ public final class DubboBootstrap {
 
     protected final Environment environment;
 
-    protected ReferenceCache referenceCache;
-
     private ApplicationDeployer applicationDeployer;
 
     /**
@@ -106,23 +108,27 @@ public final class DubboBootstrap {
         if (instance == null) {
             synchronized (DubboBootstrap.class) {
                 if (instance == null) {
-                    instance = new DubboBootstrap(ApplicationModel.defaultModel());
+                    instance = DubboBootstrap.getInstance(ApplicationModel.defaultModel());
                 }
             }
         }
         return instance;
     }
 
+    public static DubboBootstrap getInstance(ApplicationModel applicationModel) {
+        return instanceMap.computeIfAbsent(applicationModel, _k -> new DubboBootstrap(applicationModel));
+    }
+
     public static DubboBootstrap newInstance() {
-        return new DubboBootstrap(FrameworkModel.defaultModel());
+        return getInstance(FrameworkModel.defaultModel().newApplication());
     }
 
     public static DubboBootstrap newInstance(FrameworkModel frameworkModel) {
-        return new DubboBootstrap(frameworkModel);
+        return getInstance(frameworkModel.newApplication());
     }
 
     public static DubboBootstrap newInstance(ApplicationModel applicationModel) {
-        return new DubboBootstrap(applicationModel);
+        return getInstance(applicationModel);
     }
 
     /**
@@ -147,7 +153,7 @@ public final class DubboBootstrap {
                 instance.destroy();
                 instance = null;
             }
-            MetadataReportInstance.reset();
+            AbstractMetadataReportFactory.destroy();
             destroyAllProtocols();
             FrameworkModel.destroyAll();
         } else {
@@ -157,22 +163,49 @@ public final class DubboBootstrap {
         ApplicationModel.reset();
     }
 
-    private DubboBootstrap(FrameworkModel frameworkModel) {
-        this(new ApplicationModel(frameworkModel));
-    }
-
     private DubboBootstrap(ApplicationModel applicationModel) {
         this.applicationModel = applicationModel;
         configManager = applicationModel.getApplicationConfigManager();
         environment = applicationModel.getModelEnvironment();
 
-        referenceCache = new CompositeReferenceCache(applicationModel);
         executorRepository = applicationModel.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
         applicationDeployer = applicationModel.getDeployer();
+        // listen deploy events
+        applicationDeployer.addDeployListener(new DeployListenerAdapter<ApplicationModel>() {
+            @Override
+            public void onStarted(ApplicationModel scopeModel) {
+                notifyStarted(applicationModel);
+            }
+
+            @Override
+            public void onStopped(ApplicationModel scopeModel) {
+                notifyStopped(applicationModel);
+            }
+
+            @Override
+            public void onFailure(ApplicationModel scopeModel, Throwable cause) {
+                notifyStopped(applicationModel);
+            }
+        });
         // register DubboBootstrap bean
         applicationModel.getBeanFactory().registerBean(this);
     }
 
+    private void notifyStarted(ApplicationModel applicationModel) {
+        ExtensionLoader<DubboBootstrapStartStopListener> exts = applicationModel.getExtensionLoader(DubboBootstrapStartStopListener.class);
+        exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(DubboBootstrap.this));
+    }
+
+    private void notifyStopped(ApplicationModel applicationModel) {
+        ExtensionLoader<DubboBootstrapStartStopListener> exts = applicationModel.getExtensionLoader(DubboBootstrapStartStopListener.class);
+        exts.getSupportedExtensionInstances().forEach(ext -> ext.onStop(DubboBootstrap.this));
+        executeMutually(() -> {
+            awaited.set(true);
+            condition.signalAll();
+        });
+        instanceMap.remove(applicationModel);
+    }
+
     /**
      * Initialize
      */
@@ -181,13 +214,35 @@ public final class DubboBootstrap {
     }
 
     /**
-     * Start the bootstrap
+     * Start dubbo application and wait for finish
      */
     public DubboBootstrap start() {
-        applicationDeployer.start();
+        this.start(true);
+        return this;
+    }
+
+    /**
+     * Start dubbo application
+     * @param wait If true, wait for startup to complete, or else no waiting.
+     * @return
+     */
+    public DubboBootstrap start(boolean wait) {
+        CompletableFuture future = applicationDeployer.start();
+        if (wait) {
+            try {
+                future.get();
+            } catch (Exception e) {
+                throw new IllegalStateException("await dubbo application start finish failure", e);
+            }
+        }
         return this;
     }
 
+    /**
+     * Stop dubbo application
+     * @return
+     * @throws IllegalStateException
+     */
     public DubboBootstrap stop() throws IllegalStateException {
         destroy();
         return this;
@@ -201,16 +256,48 @@ public final class DubboBootstrap {
         return applicationDeployer.isInitialized();
     }
 
+    public boolean isPending() {
+        return applicationDeployer.isPending();
+    }
+
+    /**
+     * @return true if the dubbo application is starting or has been started.
+     */
+    public boolean isRunning() {
+        return applicationDeployer.isRunning();
+    }
+
+    /**
+     * @return true if the dubbo application is starting.
+     * @see #isStarted()
+     */
+    public boolean isStarting() {
+        return applicationDeployer.isStarting();
+    }
+
+    /**
+     * @return true if the dubbo application has been started.
+     * @see #start()
+     * @see #isStarting()
+     */
     public boolean isStarted() {
         return applicationDeployer.isStarted();
     }
 
-    public boolean isStartup() {
-        return applicationDeployer.isStartup();
+    /**
+     * @return true if the dubbo application is stopping.
+     * @see #isStopped()
+     */
+    public boolean isStopping() {
+        return applicationDeployer.isStopping();
     }
 
-    public boolean isShutdown() {
-        return applicationDeployer.isShutdown();
+    /**
+     * @return true if the dubbo application is stopping.
+     * @see #isStopped()
+     */
+    public boolean isStopped() {
+        return applicationDeployer.isStopped();
     }
 
     /**
@@ -221,7 +308,7 @@ public final class DubboBootstrap {
     public DubboBootstrap await() {
         // if has been waited, no need to wait again, return immediately
         if (!awaited.get()) {
-            if (!isShutdown()) {
+            if (!isStopped()) {
                 executeMutually(() -> {
                     while (!awaited.get()) {
                         if (logger.isInfoEnabled()) {
@@ -240,7 +327,7 @@ public final class DubboBootstrap {
     }
 
     public ReferenceCache getCache() {
-        return referenceCache;
+        return applicationDeployer.getReferenceCache();
     }
 
     /**
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java
index dc7818d..0befe7d 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrapStartStopListener.java
@@ -26,6 +26,7 @@ import org.apache.dubbo.common.extension.SPI;
  * @see DubboBootstrap
  */
 @SPI
+@Deprecated
 public interface DubboBootstrapStartStopListener {
 
     void onStart(DubboBootstrap bootstrap);
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DefaultApplicationDeployer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java
similarity index 81%
rename from dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DefaultApplicationDeployer.java
rename to dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java
index 248932a..86c35b4 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DefaultApplicationDeployer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config.bootstrap;
+package org.apache.dubbo.config.deploy;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.config.ConfigurationUtils;
@@ -23,9 +23,12 @@ import org.apache.dubbo.common.config.ReferenceCache;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
 import org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory;
 import org.apache.dubbo.common.config.configcenter.wrapper.CompositeDynamicConfiguration;
+import org.apache.dubbo.common.deploy.AbstractDeployer;
+import org.apache.dubbo.common.deploy.ApplicationDeployListener;
 import org.apache.dubbo.common.deploy.ApplicationDeployer;
 import org.apache.dubbo.common.deploy.ModuleDeployer;
 import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.lang.ShutdownHookCallbacks;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
@@ -65,12 +68,11 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Supplier;
 
 import static java.lang.String.format;
@@ -78,7 +80,6 @@ import static org.apache.dubbo.common.config.ConfigurationUtils.parseProperties;
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.common.constants.CommonConstants.REGISTRY_SPLIT_PATTERN;
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.function.ThrowableAction.execute;
 import static org.apache.dubbo.common.utils.StringUtils.isEmpty;
 import static org.apache.dubbo.common.utils.StringUtils.isNotEmpty;
 import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_PUBLISH_DELAY;
@@ -90,7 +91,7 @@ import static org.apache.dubbo.remoting.Constants.CLIENT_KEY;
 /**
  * initialize and start application instance
  */
-public class DefaultApplicationDeployer implements ApplicationDeployer {
+public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationModel> implements ApplicationDeployer {
 
     private static final Logger logger = LoggerFactory.getLogger(DefaultApplicationDeployer.class);
 
@@ -104,38 +105,35 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
 
     private final ExecutorRepository executorRepository;
 
-    protected AtomicBoolean initialized = new AtomicBoolean(false);
+    private volatile ServiceInstance serviceInstance;
 
-    protected AtomicBoolean started = new AtomicBoolean(false);
+    private AtomicBoolean hasPreparedApplicationInstance = new AtomicBoolean(false);
 
-    protected AtomicBoolean startup = new AtomicBoolean(true);
+    private volatile MetadataService metadataService;
 
-    protected AtomicBoolean destroyed = new AtomicBoolean(false);
+    private volatile MetadataServiceExporter metadataServiceExporter;
 
-    protected AtomicBoolean shutdown = new AtomicBoolean(false);
-
-    private final Lock destroyLock = new ReentrantLock();
-
-    protected volatile boolean isCurrentlyInStart = false;
-
-    protected volatile ServiceInstance serviceInstance;
-
-    protected AtomicBoolean hasPreparedApplicationInstance = new AtomicBoolean(false);
-
-    protected volatile MetadataService metadataService;
-
-    protected volatile MetadataServiceExporter metadataServiceExporter;
-
-    protected ScheduledFuture<?> asyncMetadataFuture;
+    private ScheduledFuture<?> asyncMetadataFuture;
     private String identifier;
+    private CompletableFuture startFuture;
+    private DubboShutdownHook dubboShutdownHook;
 
     public DefaultApplicationDeployer(ApplicationModel applicationModel) {
+        super(applicationModel);
         this.applicationModel = applicationModel;
         configManager = applicationModel.getApplicationConfigManager();
         environment = applicationModel.getModelEnvironment();
 
         referenceCache = new CompositeReferenceCache(applicationModel);
         executorRepository = getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+        dubboShutdownHook = new DubboShutdownHook(applicationModel);
+
+        // load spi listener
+        Set<ApplicationDeployListener> deployListeners = applicationModel.getExtensionLoader(ApplicationDeployListener.class)
+            .getSupportedExtensionInstances();
+        for (ApplicationDeployListener listener : deployListeners) {
+            this.addDeployListener(listener);
+        }
     }
 
     public static ApplicationDeployer get(ScopeModel moduleOrApplicationModel) {
@@ -157,7 +155,7 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
     }
 
     private void unRegisterShutdownHook() {
-        applicationModel.getBeanFactory().getBean(DubboShutdownHook.class).unregister();
+        dubboShutdownHook.unregister();
     }
 
     private boolean isRegisterConsumerInstance() {
@@ -182,33 +180,39 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
      * Initialize
      */
     @Override
-    public synchronized void initialize() {
-        if (!initialized.compareAndSet(false, true)) {
+    public void initialize() {
+        if (initialized.get()) {
             return;
         }
-        // register shutdown hook
-        registerShutdownHook();
+        // Ensure that the initialization is completed when concurrent calls
+        synchronized (this) {
+            if (initialized.get()) {
+                return;
+            }
+            // register shutdown hook
+            registerShutdownHook();
 
-        startConfigCenter();
+            startConfigCenter();
 
-        loadApplicationConfigs();
+            loadApplicationConfigs();
 
-        initModuleDeployers();
+            initModuleDeployers();
 
-        // @since 2.7.8
-        startMetadataCenter();
+            // @since 2.7.8
+            startMetadataCenter();
 
-        initMetadataService();
+            initMetadataService();
 
-        if (logger.isInfoEnabled()) {
-            logger.info(getIdentifier() + " has been initialized!");
+            initialized.set(true);
+
+            if (logger.isInfoEnabled()) {
+                logger.info(getIdentifier() + " has been initialized!");
+            }
         }
     }
 
     private void registerShutdownHook() {
-        applicationModel.getBeanFactory().getBean(DubboShutdownHook.class)
-            .addCallback(DefaultApplicationDeployer.this::destroy)
-            .register();
+        dubboShutdownHook.register();
     }
 
     private void initModuleDeployers() {
@@ -504,74 +508,61 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
 
     /**
      * Start the bootstrap
+     *
+     * @return
      */
     @Override
-    public synchronized void start() {
-        // avoid re-entry start method multiple times in same thread
-        if (isCurrentlyInStart) {
-            return;
+    public synchronized CompletableFuture start() {
+        if (isStarting()) {
+            return startFuture;
+        }
+        startFuture = new CompletableFuture();
+        if (isStarted()) {
+            // maybe call start again after add new module, check if any new module
+            boolean hasNewModule = false;
+            for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
+                if (moduleModel.getDeployer().isPending()) {
+                    hasNewModule = true;
+                    break;
+                }
+            }
+            // if no new module, just return
+            if (!hasNewModule) {
+                startFuture.complete(false);
+                return startFuture;
+            }
         }
 
-        isCurrentlyInStart = true;
-        try {
-            if (started.compareAndSet(false, true)) {
-                startup.set(false);
-                shutdown.set(false);
-
-                initialize();
+        onStarting();
 
-                if (logger.isInfoEnabled()) {
-                    logger.info(getIdentifier() + " is starting...");
-                }
+        initialize();
 
-                doStart();
+        doStart();
 
-                if (logger.isInfoEnabled()) {
-                    logger.info(getIdentifier() + " has started.");
-                }
-            } else {
-                if (logger.isInfoEnabled()) {
-                    logger.info(getIdentifier() + " is started, export/refer new services.");
-                }
-
-                doStart();
-
-                if (logger.isInfoEnabled()) {
-                    logger.info(getIdentifier() + " finish export/refer new services.");
-                }
-            }
-        } finally {
-            isCurrentlyInStart = false;
-        }
+        return startFuture;
     }
 
     private void doStart() {
         // copy current modules, ignore new module during starting
         List<ModuleModel> moduleModels = new ArrayList<>(applicationModel.getModuleModels());
-        List<ModuleDeployer> moduleDeployers = new ArrayList<>(moduleModels.size());
         List<CompletableFuture> futures = new ArrayList<>(moduleModels.size());
 
         for (ModuleModel moduleModel : moduleModels) {
             // export services in module
-            ModuleDeployer moduleDeployer = moduleModel.getDeployer();
-            moduleDeployers.add(moduleDeployer);
-            CompletableFuture moduleFuture = moduleDeployer.start();
-            futures.add(moduleFuture);
+            if (moduleModel.getDeployer().isPending()) {
+                CompletableFuture moduleFuture = moduleModel.getDeployer().start();
+                futures.add(moduleFuture);
+            }
         }
 
         // prepare application instance
         prepareApplicationInstance();
 
-        // wait async export / refer finish if needed
-        if (isExportBackground(moduleDeployers) || isReferBackground(moduleDeployers)) {
-            new Thread(() -> {
-                awaitDeployFinished(futures);
-                onStarted();
-            }).start();
-        } else {
-            awaitDeployFinished(futures);
-            onStarted();
-        }
+        // notify on each module started
+//        executorRepository.getSharedExecutor().submit(()-> {
+//            awaitDeployFinished(futures);
+//            onStarted();
+//        });
     }
 
     private void awaitDeployFinished(List<CompletableFuture> futures) {
@@ -593,71 +584,41 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
             if (!hasPreparedApplicationInstance.compareAndSet(false, true)) {
                 return;
             }
-            // export MetadataService
-            exportMetadataService();
-            // start internal module
-            ModuleDeployer internalModuleDeployer = applicationModel.getInternalModule().getDeployer();
-            CompletableFuture internalModuleFuture = internalModuleDeployer.start();
-            try {
-                internalModuleFuture.get();
-            } catch (Exception e) {
-                logger.warn("Internal module start waiting was interrupted");
-            }
+            prepareInternalModule();
             // register the local ServiceInstance if required
             registerServiceInstance();
         }
     }
 
-    private boolean hasExportedServices() {
-        for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
-            if (CollectionUtils.isNotEmpty(moduleModel.getServiceRepository().getExportedServices())) {
-                return true;
-            }
+    private void prepareInternalModule() {
+        // export MetadataService
+        exportMetadataService();
+        // start internal module
+        ModuleDeployer internalModuleDeployer = applicationModel.getInternalModule().getDeployer();
+        if (!internalModuleDeployer.isRunning()) {
+            internalModuleDeployer.start();
         }
-        return false;
     }
 
-    private boolean isExportBackground(List<ModuleDeployer> moduleDeployers) {
-        // move export background option to application?
-        for (ModuleDeployer moduleDeployer : moduleDeployers) {
-            if (moduleDeployer.isExportBackground()) {
+    private boolean hasExportedServices() {
+        for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
+            if (CollectionUtils.isNotEmpty(moduleModel.getConfigManager().getServices())) {
                 return true;
             }
         }
         return false;
     }
 
-    private boolean isReferBackground(List<ModuleDeployer> moduleDeployers) {
-        // move refer background option to application?
-        for (ModuleDeployer moduleDeployer : moduleDeployers) {
-            if (moduleDeployer.isReferBackground()) {
+    @Override
+    public boolean isAsync() {
+        for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
+            if (moduleModel.getDeployer().isAsync()) {
                 return true;
             }
         }
         return false;
     }
 
-    @Override
-    public boolean isInitialized() {
-        return initialized.get();
-    }
-
-    @Override
-    public boolean isStarted() {
-        return started.get();
-    }
-
-    @Override
-    public boolean isStartup() {
-        return startup.get();
-    }
-
-    @Override
-    public boolean isShutdown() {
-        return shutdown.get();
-    }
-
-
     private DynamicConfiguration prepareEnvironment(ConfigCenterConfig configCenter) {
         if (configCenter.isValid()) {
             if (!configCenter.checkOrUpdateInitialized(true)) {
@@ -816,59 +777,99 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
     }
 
     @Override
-    public void destroy() {
-        if (destroyLock.tryLock()
-            && shutdown.compareAndSet(false, true)) {
-            try {
-                if (destroyed.compareAndSet(false, true)) {
-                    if (started.compareAndSet(true, false)) {
-                        unregisterServiceInstance();
-                        unexportMetadataService();
-                        if (asyncMetadataFuture != null) {
-                            asyncMetadataFuture.cancel(true);
-                        }
-                    }
+    public void stop() {
+        destroy();
+    }
 
-                    destroyRegistries();
-                    destroyServiceDiscoveries();
-                    destroyMetadataReports();
+    @Override
+    public synchronized void destroy() {
+        if (isStopping() || isStopped()) {
+            return;
+        }
+        try {
+            onStopping();
+            unRegisterShutdownHook();
+            unregisterServiceInstance();
+            unexportMetadataService();
+            if (asyncMetadataFuture != null) {
+                asyncMetadataFuture.cancel(true);
+            }
 
-                    applicationModel.destroy();
-                    destroyProtocols(applicationModel.getFrameworkModel());
-                    destroyExecutorRepository();
+            executeShutdownCallbacks();
 
-                    onStop();
-                }
+            applicationModel.destroy();
 
-                destroyDynamicConfigurations();
-            } catch (Throwable ignored) {
-                // ignored
-                logger.warn(ignored.getMessage(), ignored);
-            } finally {
-                initialized.set(false);
-                startup.set(false);
-                destroyLock.unlock();
-                unRegisterShutdownHook();
+            destroyProtocols();
+
+            destroyRegistries();
+            destroyServiceDiscoveries();
+            destroyMetadataReports();
+
+            destroyServiceDiscoveries();
+            destroyExecutorRepository();
+            destroyDynamicConfigurations();
+
+            onStopped();
+        } catch (Throwable ex) {
+            logger.error(getIdentifier() + " an error occurred while stopping application: " + ex.getMessage(), ex);
+            setFailed(ex);
+        }
+    }
+
+    private void executeShutdownCallbacks() {
+        ShutdownHookCallbacks shutdownHookCallbacks = applicationModel.getBeanFactory().getBean(ShutdownHookCallbacks.class);
+        shutdownHookCallbacks.callback();
+    }
+
+    @Override
+    public void checkStarting() {
+        if (isStarting()) {
+            return;
+        }
+        onStarting();
+    }
+
+    @Override
+    public void checkStarted() {
+        for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
+            if (!moduleModel.getDeployer().isStarted()) {
+                return;
             }
+        }
+        // all modules has been started
+        onStarted();
+    }
 
-            applicationModel.destroy();
+    private void onStarting() {
+        setStarting();
+        if (logger.isInfoEnabled()) {
+            logger.info(getIdentifier() + " is starting.");
         }
     }
 
     private void onStarted() {
-        startup.set(true);
+        setStarted();
         if (logger.isInfoEnabled()) {
             logger.info(getIdentifier() + " is ready.");
         }
-        DubboBootstrap dubboBootstrap = applicationModel.getBeanFactory().getBean(DubboBootstrap.class);
-        ExtensionLoader<DubboBootstrapStartStopListener> exts = getExtensionLoader(DubboBootstrapStartStopListener.class);
-        exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(dubboBootstrap));
+        if (startFuture != null) {
+            startFuture.complete(true);
+        }
     }
 
-    private void onStop() {
-        DubboBootstrap dubboBootstrap = applicationModel.getBeanFactory().getBean(DubboBootstrap.class);
-        ExtensionLoader<DubboBootstrapStartStopListener> exts = getExtensionLoader(DubboBootstrapStartStopListener.class);
-        exts.getSupportedExtensionInstances().forEach(ext -> ext.onStop(dubboBootstrap));
+    private void onStopping() {
+        setStopping();
+        if (logger.isInfoEnabled()) {
+            logger.info(getIdentifier() + " is stopping.");
+        }
+    }
+
+    private void onStopped() {
+        setStopped();
+        if (logger.isInfoEnabled()) {
+            logger.info(getIdentifier() + " has stopped.");
+        }
+
     }
 
     private void destroyExecutorRepository() {
@@ -882,7 +883,8 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
     /**
      * Destroy all the protocols.
      */
-    private static void destroyProtocols(FrameworkModel frameworkModel) {
+    private void destroyProtocols() {
+        FrameworkModel frameworkModel = applicationModel.getFrameworkModel();
         if (frameworkModel.getApplicationModels().isEmpty()) {
             //TODO destroy protocol in framework scope
             ExtensionLoader<Protocol> loader = frameworkModel.getExtensionLoader(Protocol.class);
@@ -899,16 +901,10 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
         }
     }
 
-    private static void destroyAllProtocols() {
-        for (FrameworkModel frameworkModel : FrameworkModel.getAllInstances()) {
-            destroyProtocols(frameworkModel);
-        }
-    }
-
     private void destroyServiceDiscoveries() {
         RegistryManager.getInstance(applicationModel).getServiceDiscoveries().forEach(serviceDiscovery -> {
             try {
-                execute(serviceDiscovery::destroy);
+                serviceDiscovery.destroy();
             } catch (Throwable ignored) {
                 logger.warn(ignored.getMessage(), ignored);
             }
@@ -919,11 +915,12 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
     }
 
     private void destroyMetadataReports() {
+        // TODO only destroy MetadataReport of this application
         AbstractMetadataReportFactory.destroy();
-        MetadataReportInstance.reset();
     }
 
     private void destroyDynamicConfigurations() {
+        // TODO only destroy DynamicConfiguration of this application
         // DynamicConfiguration may be cached somewhere, and maybe used during destroy
         // destroy them may cause some troubles, so just clear instances cache
         // ExtensionLoader.resetExtensionLoader(DynamicConfigurationFactory.class);
@@ -935,7 +932,11 @@ public class DefaultApplicationDeployer implements ApplicationDeployer {
 
     private String getIdentifier() {
         if (identifier == null) {
-            identifier = "Dubbo Application-" + applicationModel.getInternalId();
+            if (applicationModel.getModelName() != null && !StringUtils.isEquals(applicationModel.getModelName(), applicationModel.getInternalName())) {
+                identifier = applicationModel.getModelName() + "[" + applicationModel.getInternalId() + "]";
+            } else {
+                identifier = "Dubbo Application" + "[" + applicationModel.getInternalId() + "]";
+            }
         }
         return identifier;
     }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DefaultModuleDeployer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java
similarity index 70%
rename from dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DefaultModuleDeployer.java
rename to dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java
index 1131741..3fb206d 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DefaultModuleDeployer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultModuleDeployer.java
@@ -14,14 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config.bootstrap;
+package org.apache.dubbo.config.deploy;
 
 import org.apache.dubbo.common.config.ReferenceCache;
+import org.apache.dubbo.common.deploy.AbstractDeployer;
 import org.apache.dubbo.common.deploy.ApplicationDeployer;
+import org.apache.dubbo.common.deploy.ModuleDeployListener;
 import org.apache.dubbo.common.deploy.ModuleDeployer;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
+import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.ConsumerConfig;
 import org.apache.dubbo.config.ProviderConfig;
 import org.apache.dubbo.config.ReferenceConfig;
@@ -29,20 +32,21 @@ import org.apache.dubbo.config.ServiceConfig;
 import org.apache.dubbo.config.ServiceConfigBase;
 import org.apache.dubbo.config.context.ModuleConfigManager;
 import org.apache.dubbo.config.utils.SimpleReferenceCache;
-import org.apache.dubbo.rpc.model.ModelConstants;
+import org.apache.dubbo.rpc.model.ConsumerModel;
 import org.apache.dubbo.rpc.model.ModuleModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Export/refer services of module
  */
-public class DefaultModuleDeployer implements ModuleDeployer {
+public class DefaultModuleDeployer extends AbstractDeployer<ModuleModel> implements ModuleDeployer {
 
     private static final Logger logger = LoggerFactory.getLogger(DefaultModuleDeployer.class);
 
@@ -61,36 +65,53 @@ public class DefaultModuleDeployer implements ModuleDeployer {
     private final SimpleReferenceCache referenceCache;
     private String identifier;
 
-    private volatile boolean startup;
-
-    protected AtomicBoolean initialized = new AtomicBoolean(false);
-
     private ApplicationDeployer applicationDeployer;
+    private CompletableFuture startFuture;
+    private Boolean async;
 
-    public static ModuleDeployer get(ModuleModel moduleModel) {
-        return moduleModel.getAttribute(ModelConstants.DEPLOYER, DefaultModuleDeployer.class);
-    }
 
     public DefaultModuleDeployer(ModuleModel moduleModel) {
+        super(moduleModel);
         this.moduleModel = moduleModel;
         configManager = moduleModel.getConfigManager();
         executorRepository = moduleModel.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
         referenceCache = SimpleReferenceCache.newCache();
         applicationDeployer = DefaultApplicationDeployer.get(moduleModel);
+
+        //load spi listener
+        Set<ModuleDeployListener> listeners = moduleModel.getExtensionLoader(ModuleDeployListener.class).getSupportedExtensionInstances();
+        for (ModuleDeployListener listener : listeners) {
+            this.addDeployListener(listener);
+        }
     }
 
     @Override
     public void initialize() throws IllegalStateException {
-        if (!initialized.compareAndSet(false, true)) {
+        if (initialized.get()) {
             return;
         }
-        loadConfigs();
+        // Ensure that the initialization is completed when concurrent calls
+        synchronized (this) {
+            if (initialized.get()) {
+                return;
+            }
+            loadConfigs();
+
+            initialized.set(true);
+            if (logger.isInfoEnabled()) {
+                logger.info(getIdentifier() + " has been initialized!");
+            }
+        }
     }
 
     @Override
-    public CompletableFuture start() throws IllegalStateException {
+    public synchronized CompletableFuture start() throws IllegalStateException {
+        if (isStarting() || isStarted()) {
+            return startFuture;
+        }
 
-        CompletableFuture startFuture = new CompletableFuture();
+        onModuleStarting();
+        startFuture = new CompletableFuture();
 
         applicationDeployer.initialize();
 
@@ -109,9 +130,16 @@ public class DefaultModuleDeployer implements ModuleDeployer {
         referServices();
 
         executorRepository.getSharedExecutor().submit(() -> {
-            awaitFinish();
+
+            // wait for export finish
+            waitExportFinish();
+
+            // wait for refer finish
+            waitReferFinish();
+
             onModuleStarted(startFuture);
         });
+
         return startFuture;
     }
 
@@ -120,29 +148,74 @@ public class DefaultModuleDeployer implements ModuleDeployer {
     }
 
     @Override
-    public void destroy() throws IllegalStateException {
+    public void stop() throws IllegalStateException {
+        destroy();
+    }
+
+    @Override
+    public synchronized void destroy() throws IllegalStateException {
+        if (isStopping() || isStopped()) {
+            return;
+        }
+        onModuleStopping();
         unexportServices();
         unreferServices();
+
+        ModuleServiceRepository serviceRepository = moduleModel.getServiceRepository();
+        if (serviceRepository != null) {
+            List<ConsumerModel> consumerModels = serviceRepository.getReferredServices();
+
+            for (ConsumerModel consumerModel : consumerModels) {
+                try {
+                    if (consumerModel.getReferenceConfig() != null) {
+                        consumerModel.getReferenceConfig().destroy();
+                    } else if (consumerModel.getDestroyCaller() != null) {
+                        consumerModel.getDestroyCaller().call();
+                    }
+                } catch (Throwable t) {
+                    logger.error("Unable to destroy consumerModel.", t);
+                }
+            }
+
+            List<ProviderModel> exportedServices = serviceRepository.getExportedServices();
+            for (ProviderModel providerModel : exportedServices) {
+                try {
+                    if (providerModel.getServiceConfig() != null) {
+                        providerModel.getServiceConfig().unexport();
+                    } else if (providerModel.getDestroyCaller() != null) {
+                        providerModel.getDestroyCaller().call();
+                    }
+                } catch (Throwable t) {
+                    logger.error("Unable to destroy providerModel.", t);
+                }
+            }
+            serviceRepository.destroy();
+        }
+        moduleModel.destroy();
         onModuleStopped();
     }
 
-    private void onModuleStopped() {
-        startup = false;
-        logger.info(getIdentifier() + " has stopped.");
-        Set<ModuleDeployListener> listeners = moduleModel.getExtensionLoader(ModuleDeployListener.class).getSupportedExtensionInstances();
-        for (ModuleDeployListener listener : listeners) {
-            listener.onModuleStopped(moduleModel);
-        }
+    private void onModuleStarting() {
+        setStarting();
+        logger.info(getIdentifier() + " is starting.");
+        applicationDeployer.checkStarting();
     }
 
     private void onModuleStarted(CompletableFuture startFuture) {
-        startup = true;
+        setStarted();
         logger.info(getIdentifier() + " has started.");
         startFuture.complete(true);
-        Set<ModuleDeployListener> listeners = moduleModel.getExtensionLoader(ModuleDeployListener.class).getSupportedExtensionInstances();
-        for (ModuleDeployListener listener : listeners) {
-            listener.onModuleStarted(moduleModel);
-        }
+        applicationDeployer.checkStarted();
+    }
+
+    private void onModuleStopping() {
+        setStopping();
+        logger.info(getIdentifier() + " is stopping.");
+    }
+
+    private void onModuleStopped() {
+        setStopped();
+        logger.info(getIdentifier() + " has stopped.");
     }
 
     private void loadConfigs() {
@@ -196,6 +269,7 @@ public class DefaultModuleDeployer implements ModuleDeployer {
                 // ignored
             }
         });
+        exportedServices.clear();
 
         asyncExportingFutures.forEach(future -> {
             if (!future.isDone()) {
@@ -278,23 +352,15 @@ public class DefaultModuleDeployer implements ModuleDeployer {
         }
     }
 
-    private void awaitFinish() {
-        waitExportFinish();
-        waitReferFinish();
-    }
-
     @Override
-    public boolean isStartup() {
-        return startup;
-    }
-
-    @Override
-    public boolean isInitialized() {
-        return initialized.get();
+    public boolean isAsync() {
+        if (async == null) {
+            async = isExportBackground() || isReferBackground();
+        }
+        return async;
     }
 
-    @Override
-    public boolean isExportBackground() {
+    private boolean isExportBackground() {
         return moduleModel.getConfigManager().getProviders()
             .stream()
             .map(ProviderConfig::getExportBackground)
@@ -303,8 +369,7 @@ public class DefaultModuleDeployer implements ModuleDeployer {
             .isPresent();
     }
 
-    @Override
-    public boolean isReferBackground() {
+    private boolean isReferBackground() {
         return moduleModel.getConfigManager().getConsumers()
             .stream()
             .map(ConsumerConfig::getReferBackground)
@@ -315,7 +380,11 @@ public class DefaultModuleDeployer implements ModuleDeployer {
 
     private String getIdentifier() {
         if (identifier == null) {
-            identifier = "Dubbo Module-" + moduleModel.getInternalId();
+            if (moduleModel.getModelName() != null && !StringUtils.isEquals(moduleModel.getModelName(), moduleModel.getInternalName())) {
+                identifier = moduleModel.getModelName() + "[" + moduleModel.getInternalId() + "]";
+            } else {
+                identifier = "Dubbo Module" + "[" + moduleModel.getInternalId() + "]";
+            }
         }
         return identifier;
     }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
index a1d30b3..b08b31b 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
@@ -93,7 +93,7 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
             serviceConfig.setMethods(generateMethodConfig());
 
             // export
-            serviceConfig.export();
+            serviceConfig.exportOnly();
 
             if (logger.isInfoEnabled()) {
                 logger.info("The MetadataService exports urls : " + serviceConfig.getExportedUrls());
@@ -146,7 +146,7 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
     }
 
     public boolean isExported() {
-        return serviceConfig != null && serviceConfig.isExported();
+        return serviceConfig != null && serviceConfig.isExported() && !serviceConfig.isUnexported();
     }
 
     private ApplicationConfig getApplicationConfig() {
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapMultiInstanceTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapMultiInstanceTest.java
index 76e2df4..a89b459 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapMultiInstanceTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapMultiInstanceTest.java
@@ -34,7 +34,6 @@ import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.model.FrameworkServiceRepository;
 import org.apache.dubbo.rpc.model.ModuleModel;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -103,6 +102,7 @@ public class DubboBootstrapMultiInstanceTest {
             configProviderApp(dubboBootstrap).start();
         } finally {
             dubboBootstrap.destroy();
+            DubboBootstrap.reset();
         }
     }
 
@@ -114,9 +114,11 @@ public class DubboBootstrapMultiInstanceTest {
             configConsumerApp(dubboBootstrap).start();
             testConsumer(dubboBootstrap);
         } catch (Exception e) {
-            Assertions.assertTrue(e.toString().contains("No provider available from registry"), StringUtils.toString(e));
+            Assertions.assertTrue(e.toString().contains("No provider available"), StringUtils.toString(e));
         } finally {
             dubboBootstrap.destroy();
+            DubboBootstrap.reset();
+            SysProps.clear();
         }
     }
 
@@ -132,6 +134,7 @@ public class DubboBootstrapMultiInstanceTest {
             testConsumer(dubboBootstrap);
         } finally {
             dubboBootstrap.destroy();
+            DubboBootstrap.reset();
         }
     }
 
@@ -291,9 +294,9 @@ public class DubboBootstrapMultiInstanceTest {
 
             ModuleDeployer moduleDeployer1 = serviceConfig1.getScopeModel().getDeployer();
             moduleDeployer1.start().get();
-            Assertions.assertTrue(moduleDeployer1.isStartup());
+            Assertions.assertTrue(moduleDeployer1.isStarted());
             ModuleDeployer internalModuleDeployer = applicationModel.getInternalModule().getDeployer();
-            Assertions.assertTrue(internalModuleDeployer.isStartup());
+            Assertions.assertTrue(internalModuleDeployer.isStarted());
 
             FrameworkServiceRepository frameworkServiceRepository = applicationModel.getFrameworkModel().getServiceRepository();
             Assertions.assertNotNull(frameworkServiceRepository.lookupExportedServiceWithoutGroup(serviceKey1));
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapTest.java
index 0f76fc9..4d8031c 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapTest.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.url.component.ServiceConfigURL;
 import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.config.AbstractInterfaceConfig;
 import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.config.MetadataReportConfig;
@@ -30,10 +31,12 @@ import org.apache.dubbo.config.RegistryConfig;
 import org.apache.dubbo.config.ServiceConfig;
 import org.apache.dubbo.config.SysProps;
 import org.apache.dubbo.config.api.DemoService;
+import org.apache.dubbo.config.deploy.DefaultApplicationDeployer;
 import org.apache.dubbo.config.provider.impl.DemoServiceImpl;
 import org.apache.dubbo.config.utils.ConfigValidationUtils;
 import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.MetadataService;
+import org.apache.dubbo.metadata.MetadataServiceExporter;
 import org.apache.dubbo.metadata.WritableMetadataService;
 import org.apache.dubbo.monitor.MonitorService;
 import org.apache.dubbo.registry.RegistryService;
@@ -255,12 +258,12 @@ public class DubboBootstrapTest {
 
         Assertions.assertTrue(bootstrap.isInitialized());
         Assertions.assertTrue(bootstrap.isStarted());
-        Assertions.assertFalse(bootstrap.isShutdown());
+        Assertions.assertFalse(bootstrap.isStopped());
 
         ApplicationModel applicationModel = bootstrap.getApplicationModel();
         DefaultApplicationDeployer applicationDeployer = getApplicationDeployer(applicationModel);
-        Assertions.assertNotNull(applicationDeployer.serviceInstance);
-        Assertions.assertNotNull(applicationDeployer.asyncMetadataFuture);
+        Assertions.assertNotNull(ReflectUtils.getFieldValue(applicationDeployer, "serviceInstance"));
+        Assertions.assertNotNull(ReflectUtils.getFieldValue(applicationDeployer, "asyncMetadataFuture"));
         Assertions.assertTrue(applicationModel.getDefaultModule().getServiceRepository().getExportedServices().size() > 0);
     }
 
@@ -333,7 +336,8 @@ public class DubboBootstrapTest {
 
     private void assertMetadataService(DubboBootstrap bootstrap, int availablePort, boolean shouldReport) {
         DefaultApplicationDeployer applicationDeployer = getApplicationDeployer(bootstrap.getApplicationModel());
-        Assertions.assertTrue(applicationDeployer.metadataServiceExporter.isExported());
+        MetadataServiceExporter metadataServiceExporter = ReflectUtils.getFieldValue(applicationDeployer, "metadataServiceExporter");
+        Assertions.assertTrue(metadataServiceExporter.isExported());
         DubboProtocol protocol = DubboProtocol.getDubboProtocol(bootstrap.getApplicationModel());
         Map<String, Exporter<?>> exporters = protocol.getExporterMap();
         Assertions.assertEquals(2, exporters.size());
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java
index 969cd5b..862a553 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/integration/single/SingleRegistryCenterDubboProtocolIntegrationTest.java
@@ -42,7 +42,6 @@ import org.apache.dubbo.registrycenter.RegistryCenter;
 import org.apache.dubbo.registrycenter.ZookeeperSingleRegistryCenter;
 import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.model.ApplicationModel;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -162,12 +161,14 @@ public class SingleRegistryCenterDubboProtocolIntegrationTest implements Integra
         Assertions.assertFalse(serviceConfig.isExported());
         // ServiceConfig's exportedUrl has values or not
         Assertions.assertEquals(serviceConfig.getExportedUrls().size(), 0);
+        // DubboBootstrap is pending or not
+        Assertions.assertTrue(DubboBootstrap.getInstance().isPending());
         // DubboBootstrap is initialized or not
         Assertions.assertFalse(DubboBootstrap.getInstance().isInitialized());
         // DubboBootstrap is started or not
         Assertions.assertFalse(DubboBootstrap.getInstance().isStarted());
-        // DubboBootstrap is shutdown or not
-        Assertions.assertFalse(DubboBootstrap.getInstance().isShutdown());
+        // DubboBootstrap is stopped or not
+        Assertions.assertFalse(DubboBootstrap.getInstance().isStopped());
         // The ServiceListener is loaded by SPI or not
         Assertions.assertNull(singleRegistryCenterExportedServiceListener);
     }
@@ -202,10 +203,12 @@ public class SingleRegistryCenterDubboProtocolIntegrationTest implements Integra
     private void afterExport() {
         // DubboBootstrap is initialized or not
         Assertions.assertTrue(DubboBootstrap.getInstance().isInitialized());
+        // DubboBootstrap is pending or not
+        Assertions.assertFalse(DubboBootstrap.getInstance().isPending());
         // DubboBootstrap is started or not
         Assertions.assertTrue(DubboBootstrap.getInstance().isStarted());
         // DubboBootstrap is shutdown or not
-        Assertions.assertFalse(DubboBootstrap.getInstance().isShutdown());
+        Assertions.assertFalse(DubboBootstrap.getInstance().isStopped());
         // Service has been exported or not
         Assertions.assertTrue(this.serviceConfig.isExported());
         // There is exported urls or not
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
index 95fc792..2f95adc 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
@@ -22,15 +22,14 @@ import org.apache.dubbo.config.RegistryConfig;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.metadata.ConfigurableMetadataServiceExporter;
 import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
 import static org.apache.dubbo.common.constants.CommonConstants.COMPOSITE_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
 import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
@@ -40,21 +39,21 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
  */
 public class MetadataServiceExporterTest {
 
-    @BeforeAll
-    public static void init() {
+    @BeforeEach
+    public void init() {
         DubboBootstrap.reset();
-        ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(new ApplicationConfig("Test"));
+
+        ApplicationConfig applicationConfig = new ApplicationConfig("Test");
+        applicationConfig.setRegisterConsumer(true);
+        ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(applicationConfig);
         ApplicationModel.defaultModel().getApplicationConfigManager().addRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
         ApplicationModel.defaultModel().getApplicationConfigManager().addProtocol(new ProtocolConfig("injvm"));
     }
 
-    @AfterAll
-    public static void destroy() {
-        DubboBootstrap.reset();
-    }
-
     @Test
     public void test() {
+        ApplicationModel.defaultModel().getInternalModule().getDeployer().start();
+
         MetadataService metadataService = Mockito.mock(MetadataService.class);
         ConfigurableMetadataServiceExporter exporter = new ConfigurableMetadataServiceExporter();
         exporter.setMetadataService(metadataService);
@@ -68,4 +67,23 @@ public class MetadataServiceExporterTest {
         assertTrue(exporter.supports(REMOTE_METADATA_STORAGE_TYPE));
         assertTrue(exporter.supports(COMPOSITE_METADATA_STORAGE_TYPE));
     }
+
+    @Test
+    public void test2() throws Exception {
+
+        ApplicationModel applicationModel = ApplicationModel.defaultModel();
+        ConfigurableMetadataServiceExporter exporter = (ConfigurableMetadataServiceExporter) applicationModel.getExtensionLoader(MetadataServiceExporter.class).getDefaultExtension();
+        MetadataService metadataService = Mockito.mock(MetadataService.class);
+        exporter.setMetadataService(metadataService);
+
+        applicationModel.getDeployer().start().get();
+        assertTrue(exporter.isExported());
+        assertTrue(exporter.supports(DEFAULT_METADATA_STORAGE_TYPE));
+        assertTrue(exporter.supports(REMOTE_METADATA_STORAGE_TYPE));
+        assertTrue(exporter.supports(COMPOSITE_METADATA_STORAGE_TYPE));
+
+        applicationModel.getDeployer().stop();
+        assertFalse(exporter.isExported());
+    }
+
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java
index 6d7158f..bbddd65 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/ServiceBean.java
@@ -19,10 +19,10 @@ package org.apache.dubbo.config.spring;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.config.ServiceConfig;
 import org.apache.dubbo.config.annotation.Service;
-import org.apache.dubbo.config.spring.context.DubboSpringInitializationContext;
 import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
 import org.apache.dubbo.config.spring.util.DubboBeanUtils;
 import org.apache.dubbo.config.support.Parameter;
+import org.apache.dubbo.rpc.model.ModuleModel;
 import org.springframework.aop.support.AopUtils;
 import org.springframework.beans.factory.BeanNameAware;
 import org.springframework.beans.factory.DisposableBean;
@@ -87,9 +87,9 @@ public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean
                 setPath(getInterface());
             }
         }
-        //register service bean and set bootstrap
-        DubboSpringInitializationContext dubboContext = DubboBeanUtils.getInitializationContext(applicationContext);
-        dubboContext.getDubboBootstrap().service(this, dubboContext.getModuleModel());
+        //register service bean
+        ModuleModel moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
+        moduleModel.getConfigManager().addService(this);
     }
 
     /**
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigScopeModelInitializer.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/SpringScopeModelInitializer.java
similarity index 59%
copy from dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigScopeModelInitializer.java
copy to dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/SpringScopeModelInitializer.java
index 994116d..cd03ba7 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ConfigScopeModelInitializer.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/SpringScopeModelInitializer.java
@@ -14,20 +14,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dubbo.config;
+package org.apache.dubbo.config.spring;
 
 import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
-import org.apache.dubbo.common.deploy.ApplicationDeployer;
-import org.apache.dubbo.common.deploy.ModuleDeployer;
-import org.apache.dubbo.config.bootstrap.DefaultApplicationDeployer;
-import org.apache.dubbo.config.bootstrap.DefaultModuleDeployer;
-import org.apache.dubbo.config.utils.DefaultConfigValidator;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.model.ModuleModel;
 import org.apache.dubbo.rpc.model.ScopeModelInitializer;
 
-public class ConfigScopeModelInitializer implements ScopeModelInitializer {
+/**
+ * Register scope beans in spring module
+ */
+public class SpringScopeModelInitializer implements ScopeModelInitializer {
 
     @Override
     public void initializeFrameworkModel(FrameworkModel frameworkModel) {
@@ -37,19 +35,10 @@ public class ConfigScopeModelInitializer implements ScopeModelInitializer {
     @Override
     public void initializeApplicationModel(ApplicationModel applicationModel) {
         ScopeBeanFactory beanFactory = applicationModel.getBeanFactory();
-        beanFactory.registerBean(DubboShutdownHook.class);
-        beanFactory.registerBean(DefaultConfigValidator.class);
-        // applicationDeployer
-        ApplicationDeployer applicationDeployer = beanFactory.registerBean(DefaultApplicationDeployer.class);
-        applicationModel.setDeployer(applicationDeployer);
-
     }
 
     @Override
     public void initializeModuleModel(ModuleModel moduleModel) {
-        ScopeBeanFactory beanFactory = moduleModel.getBeanFactory();
-        // moduleDeployer
-        ModuleDeployer moduleDeployer = beanFactory.registerBean(DefaultModuleDeployer.class);
-        moduleModel.setDeployer(moduleDeployer);
+
     }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
index d099713..2fe8836 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
@@ -25,9 +25,9 @@ import org.apache.dubbo.config.annotation.DubboReference;
 import org.apache.dubbo.config.annotation.Reference;
 import org.apache.dubbo.config.context.ConfigManager;
 import org.apache.dubbo.config.spring.Constants;
-import org.apache.dubbo.config.spring.context.event.DubboAnnotationInitedEvent;
-import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
 import org.apache.dubbo.config.spring.ReferenceBean;
+import org.apache.dubbo.config.spring.context.event.DubboConfigInitEvent;
+import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
 import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
 import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
 import org.apache.dubbo.config.spring.util.SpringCompatUtils;
@@ -174,7 +174,7 @@ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBean
 
         try {
             // this is an early event, it will be notified at org.springframework.context.support.AbstractApplicationContext.registerListeners()
-            applicationContext.publishEvent(new DubboAnnotationInitedEvent(applicationContext));
+            applicationContext.publishEvent(new DubboConfigInitEvent(applicationContext));
         } catch (Exception e) {
             // if spring version is less then 4.2, it does not support early application event
             logger.warn("publish early application event failed, please upgrade spring version to 4.2.x or later: " + e);
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java
index 4192949..90e23dc 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapApplicationListener.java
@@ -18,11 +18,11 @@ package org.apache.dubbo.config.spring.context;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.dubbo.config.DubboShutdownHook;
 import org.apache.dubbo.config.bootstrap.BootstrapTakeoverMode;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.config.spring.context.event.DubboAnnotationInitedEvent;
+import org.apache.dubbo.config.spring.context.event.DubboConfigInitEvent;
 import org.apache.dubbo.config.spring.util.DubboBeanUtils;
+import org.apache.dubbo.rpc.model.ModuleModel;
 import org.springframework.beans.BeansException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
@@ -41,6 +41,7 @@ import static org.springframework.util.ObjectUtils.nullSafeEquals;
  *
  * @since 2.7.5
  */
+@Deprecated
 public class DubboBootstrapApplicationListener implements ApplicationListener, ApplicationContextAware, Ordered {
 
     /**
@@ -55,6 +56,7 @@ public class DubboBootstrapApplicationListener implements ApplicationListener, A
     private ApplicationContext applicationContext;
     private DubboBootstrap bootstrap;
     private boolean shouldInitConfigBeans;
+    private ModuleModel moduleModel;
 
     public DubboBootstrapApplicationListener() {
     }
@@ -74,7 +76,7 @@ public class DubboBootstrapApplicationListener implements ApplicationListener, A
     @Override
     public void onApplicationEvent(ApplicationEvent event) {
         if (isOriginalEventSource(event)) {
-            if (event instanceof DubboAnnotationInitedEvent) {
+            if (event instanceof DubboConfigInitEvent) {
                 // This event will be notified at AbstractApplicationContext.registerListeners(),
                 // init dubbo config beans before spring singleton beans
                 initDubboConfigBeans();
@@ -93,7 +95,7 @@ public class DubboBootstrapApplicationListener implements ApplicationListener, A
         }
 
         // All infrastructure config beans are loaded, initialize dubbo here
-        bootstrap.initialize();
+        moduleModel.getDeployer().initialize();
     }
 
     private void onApplicationContextEvent(ApplicationContextEvent event) {
@@ -110,14 +112,15 @@ public class DubboBootstrapApplicationListener implements ApplicationListener, A
 
     private void onContextRefreshedEvent(ContextRefreshedEvent event) {
         if (bootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) {
-            bootstrap.start();
+            moduleModel.getDeployer().start();
         }
     }
 
     private void onContextClosedEvent(ContextClosedEvent event) {
         if (bootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) {
             // will call dubboBootstrap.stop() through shutdown callback.
-            bootstrap.getApplicationModel().getBeanFactory().getBean(DubboShutdownHook.class).run();
+            //bootstrap.getApplicationModel().getBeanFactory().getBean(DubboShutdownHook.class).run();
+            moduleModel.getDeployer().stop();
         }
     }
 
@@ -129,11 +132,6 @@ public class DubboBootstrapApplicationListener implements ApplicationListener, A
     private boolean isOriginalEventSource(ApplicationEvent event) {
 
         boolean originalEventSource = nullSafeEquals(getApplicationContext(), event.getSource());
-//        if (!originalEventSource) {
-//            if (log.isDebugEnabled()) {
-//                log.debug("The source of event[" + event.getSource() + "] is not original!");
-//            }
-//        }
         return originalEventSource;
     }
 
@@ -145,7 +143,8 @@ public class DubboBootstrapApplicationListener implements ApplicationListener, A
     @Override
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         this.applicationContext = applicationContext;
-        this.setBootstrap(DubboBeanUtils.getBootstrap(applicationContext));
+        moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
+        this.setBootstrap(DubboBootstrap.getInstance(moduleModel.getApplicationModel()));
         if (shouldInitConfigBeans) {
             checkCallStackAndInit();
         }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java
index 3ae3dcf..0894a52 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboBootstrapStartStopListenerSpringAdapter.java
@@ -20,7 +20,6 @@ import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.bootstrap.DubboBootstrapStartStopListener;
 import org.apache.dubbo.config.spring.context.event.DubboBootstrapStatedEvent;
 import org.apache.dubbo.config.spring.context.event.DubboBootstrapStopedEvent;
-
 import org.springframework.context.ApplicationContext;
 
 /**
@@ -28,6 +27,7 @@ import org.springframework.context.ApplicationContext;
  *
  * @scene 2.7.9
  */
+@Deprecated
 public class DubboBootstrapStartStopListenerSpringAdapter implements DubboBootstrapStartStopListener {
 
     static ApplicationContext applicationContext;
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigApplicationListener.java
new file mode 100644
index 0000000..6a839e7
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboConfigApplicationListener.java
@@ -0,0 +1,72 @@
+/*
+ * 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.spring.context;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.dubbo.config.spring.context.event.DubboConfigInitEvent;
+import org.apache.dubbo.config.spring.util.DubboBeanUtils;
+import org.apache.dubbo.rpc.model.ModuleModel;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationListener;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * An ApplicationListener to load config beans
+ */
+public class DubboConfigApplicationListener implements ApplicationListener<DubboConfigInitEvent>, ApplicationContextAware {
+
+    private final static Log logger = LogFactory.getLog(DubboConfigApplicationListener.class);
+
+    private ApplicationContext applicationContext;
+
+    private ModuleModel moduleModel;
+
+    private AtomicBoolean initialized = new AtomicBoolean();
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        this.applicationContext = applicationContext;
+        this.moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
+    }
+
+    @Override
+    public void onApplicationEvent(DubboConfigInitEvent event) {
+        // It's expected to be notify at org.springframework.context.support.AbstractApplicationContext.registerListeners(),
+        // before loading non-lazy singleton beans. At this moment, all BeanFactoryPostProcessor have been processed,
+        if (initialized.compareAndSet(false, true)) {
+            initDubboConfigBeans();
+        }
+    }
+
+    private void initDubboConfigBeans() {
+        // load DubboConfigBeanInitializer to init config beans
+        if (applicationContext.containsBean(DubboConfigBeanInitializer.BEAN_NAME)) {
+            applicationContext.getBean(DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);
+        } else {
+            logger.warn("Bean '" + DubboConfigBeanInitializer.BEAN_NAME + "' was not found");
+        }
+
+        // All infrastructure config beans are loaded, initialize dubbo here
+        moduleModel.getDeployer().prepare();
+    }
+
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java
new file mode 100644
index 0000000..ad09ebd
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboDeployApplicationListener.java
@@ -0,0 +1,126 @@
+/*
+ * 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.spring.context;
+
+import org.apache.dubbo.common.deploy.DeployListenerAdapter;
+import org.apache.dubbo.common.deploy.DeployState;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.config.spring.context.event.DubboApplicationStateEvent;
+import org.apache.dubbo.config.spring.util.DubboBeanUtils;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ModelConstants;
+import org.apache.dubbo.rpc.model.ModuleModel;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ApplicationContextEvent;
+import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.core.Ordered;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * An ApplicationListener to control Dubbo application.
+ */
+public class DubboDeployApplicationListener implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {
+
+    private static final Logger logger = LoggerFactory.getLogger(DubboDeployApplicationListener.class);
+
+    private ApplicationContext applicationContext;
+
+    private ApplicationModel applicationModel;
+    private ModuleModel moduleModel;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        this.applicationContext = applicationContext;
+        this.applicationModel = DubboBeanUtils.getApplicationModel(applicationContext);
+        this.moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
+        // listen deploy events and publish DubboApplicationStateEvent
+        applicationModel.getDeployer().addDeployListener(new DeployListenerAdapter<ApplicationModel>(){
+            @Override
+            public void onStarting(ApplicationModel scopeModel) {
+                publishEvent(DeployState.STARTING);
+            }
+
+            @Override
+            public void onStarted(ApplicationModel scopeModel) {
+                publishEvent(DeployState.STARTED);
+            }
+
+            @Override
+            public void onStopping(ApplicationModel scopeModel) {
+                publishEvent(DeployState.STOPPING);
+            }
+
+            @Override
+            public void onStopped(ApplicationModel scopeModel) {
+                publishEvent(DeployState.STOPPED);
+            }
+
+            @Override
+            public void onFailure(ApplicationModel scopeModel, Throwable cause) {
+                publishEvent(DeployState.FAILED, cause);
+            }
+        });
+    }
+
+    private void publishEvent(DeployState state) {
+        applicationContext.publishEvent(new DubboApplicationStateEvent(applicationModel, state));
+    }
+
+    private void publishEvent(DeployState state, Throwable cause) {
+        applicationContext.publishEvent(new DubboApplicationStateEvent(applicationModel, state, cause));
+    }
+
+    @Override
+    public void onApplicationEvent(ApplicationContextEvent event) {
+        if (event instanceof ContextRefreshedEvent) {
+            onContextRefreshedEvent((ContextRefreshedEvent) event);
+        } else if (event instanceof ContextClosedEvent) {
+            onContextClosedEvent((ContextClosedEvent) event);
+        }
+    }
+
+    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
+        CompletableFuture future = moduleModel.getDeployer().start();
+        try {
+            future.get();
+        } catch (InterruptedException e) {
+            logger.warn("Interrupted while waiting for dubbo module start: " + e.getMessage());
+        } catch (Exception e) {
+            logger.warn("An error occurred while waiting for dubbo module start: " + e.getMessage(), e);
+        }
+    }
+
+    private void onContextClosedEvent(ContextClosedEvent event) {
+        Object value = moduleModel.getAttribute(ModelConstants.KEEP_RUNNING_ON_SPRING_CLOSED);
+        boolean keepRunningOnClosed = Boolean.parseBoolean(String.valueOf(value));
+        if (!keepRunningOnClosed) {
+            moduleModel.getDeployer().stop();
+        }
+    }
+
+    @Override
+    public int getOrder() {
+        return LOWEST_PRECEDENCE;
+    }
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
index 1b02292..ecbe4ad 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboInfraBeanRegisterPostProcessor.java
@@ -37,8 +37,9 @@ import java.util.SortedMap;
  * Register some infrastructure beans if not exists.
  * This post-processor MUST impl BeanDefinitionRegistryPostProcessor,
  * in order to enable the registered BeanFactoryPostProcessor bean to be loaded and executed.
+ *
  * @see org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(
- * org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List)
+ *org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List)
  */
 public class DubboInfraBeanRegisterPostProcessor implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
 
@@ -74,6 +75,7 @@ public class DubboInfraBeanRegisterPostProcessor implements BeanDefinitionRegist
 
         // Initialize SpringExtensionInjector
         SpringExtensionInjector.get(applicationModel).init(applicationContext);
+        DubboBeanUtils.getInitializationContext(beanFactory).setApplicationContext(applicationContext);
 
         // Initialize dubbo Environment before ConfigManager
         // Extract dubbo props from Spring env and put them to app config
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializationContext.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitContext.java
similarity index 76%
rename from dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializationContext.java
rename to dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitContext.java
index 1bca441..f2a2fd3 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializationContext.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitContext.java
@@ -16,17 +16,20 @@
  */
 package org.apache.dubbo.config.spring.context;
 
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ModelConstants;
 import org.apache.dubbo.rpc.model.ModuleModel;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 import org.springframework.context.ApplicationContext;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Dubbo spring initialization context object
  */
-public class DubboSpringInitializationContext {
+public class DubboSpringInitContext {
 
     private BeanDefinitionRegistry registry;
 
@@ -36,7 +39,7 @@ public class DubboSpringInitializationContext {
 
     private ModuleModel moduleModel;
 
-    private DubboBootstrap dubboBootstrap;
+    private Map<String, Object> moduleAttributes = new HashMap<>();
 
     private volatile boolean bound;
 
@@ -79,7 +82,6 @@ public class DubboSpringInitializationContext {
     /**
      * Change the binding ModuleModel, the ModuleModel and DubboBootstrap must be matched.
      *
-     * @see #setDubboBootstrap(DubboBootstrap)
      * @param moduleModel
      */
     public void setModuleModel(ModuleModel moduleModel) {
@@ -89,22 +91,23 @@ public class DubboSpringInitializationContext {
         this.moduleModel = moduleModel;
     }
 
-    public DubboBootstrap getDubboBootstrap() {
-        return dubboBootstrap;
+    public boolean isKeepRunningOnSpringClosed() {
+        return (boolean) moduleAttributes.get(ModelConstants.KEEP_RUNNING_ON_SPRING_CLOSED);
     }
 
     /**
-     * Change the binding DubboBootstrap instance, the ModuleModel and DubboBootstrap must be matched.
-     * <p></p>
-     * By default, DubboBoostrap is created using the ApplicationModel in which the ModuleModel resides.
-     *
-     * @see #setModuleModel(ModuleModel)
-     * @param dubboBootstrap
+     * Keep Dubbo running when spring is stopped
+     * @param keepRunningOnSpringClosed
      */
-    public void setDubboBootstrap(DubboBootstrap dubboBootstrap) {
-        if (bound) {
-            throw new IllegalStateException("Cannot change DubboBootstrap after bound context");
-        }
-        this.dubboBootstrap = dubboBootstrap;
+    public void setKeepRunningOnSpringClosed(boolean keepRunningOnSpringClosed) {
+        this.setModuleAttribute(ModelConstants.KEEP_RUNNING_ON_SPRING_CLOSED, keepRunningOnSpringClosed);
+    }
+
+    public Map<String, Object> getModuleAttributes() {
+        return moduleAttributes;
+    }
+
+    public void setModuleAttribute(String key, Object value) {
+        this.moduleAttributes.put(key, value);
     }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializationCustomizer.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitCustomizer.java
similarity index 91%
rename from dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializationCustomizer.java
rename to dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitCustomizer.java
index 223307a..fa9da0e 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializationCustomizer.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitCustomizer.java
@@ -27,12 +27,12 @@ import static org.apache.dubbo.common.extension.ExtensionScope.FRAMEWORK;
  * Custom dubbo spring initialization
  */
 @SPI(scope = FRAMEWORK)
-public interface DubboSpringInitializationCustomizer {
+public interface DubboSpringInitCustomizer {
 
     /**
      * <p>Customize dubbo spring initialization on bean registry processing phase.</p>
      * <p>You can register a {@link BeanFactoryPostProcessor} or {@link BeanPostProcessor} for custom processing.</p>
-     * <p>Or change the bind module model via {@link DubboSpringInitializationContext#setModuleModel(ModuleModel)}.</p>
+     * <p>Or change the bind module model via {@link DubboSpringInitContext#setModuleModel(ModuleModel)}.</p>
      *
      * <p><b>Note:</b></p>
      * <p>1. The bean factory may be not ready yet when triggered by parsing dubbo xml definition.</p>
@@ -41,6 +41,6 @@ public interface DubboSpringInitializationCustomizer {
      *
      * @param context
      */
-    void customize(DubboSpringInitializationContext context);
+    void customize(DubboSpringInitContext context);
 
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitCustomizerHolder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitCustomizerHolder.java
new file mode 100644
index 0000000..0e6a951
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitCustomizerHolder.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.spring.context;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Hold a set of DubboSpringInitCustomizer, for register customizers by programing.
+ * <p>All customizers are store in thread local, and they will be clear after apply once.</p>
+ *
+ * <p>Usages:</p>
+ *<pre>
+ * DubboSpringInitCustomizerHolder.get().addCustomizer(context -> {...});
+ * ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext(..);
+ * ...
+ * DubboSpringInitCustomizerHolder.get().addCustomizer(context -> {...});
+ * ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext(..);
+ * </pre>
+ */
+public class DubboSpringInitCustomizerHolder {
+
+    private static final ThreadLocal<DubboSpringInitCustomizerHolder> holders = ThreadLocal.withInitial(() ->
+        new DubboSpringInitCustomizerHolder());
+
+    public static DubboSpringInitCustomizerHolder get() {
+        return holders.get();
+    }
+
+    private Set<DubboSpringInitCustomizer> customizers = new HashSet<>();
+
+    public void addCustomizer(DubboSpringInitCustomizer customizer) {
+        this.customizers.add(customizer);
+    }
+
+    public void clearCustomizers() {
+        this.customizers = new HashSet<>();
+    }
+
+    public Set<DubboSpringInitCustomizer> getCustomizers() {
+        return customizers;
+    }
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializer.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializer.java
index 4272e3b..c8f2c24 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializer.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/DubboSpringInitializer.java
@@ -16,17 +16,12 @@
  */
 package org.apache.dubbo.config.spring.context;
 
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.config.spring.extension.SpringExtensionInjector;
 import org.apache.dubbo.config.spring.util.DubboBeanUtils;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.model.ModuleModel;
-import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.support.GenericApplicationContext;
 
 import java.util.Map;
@@ -38,35 +33,34 @@ import java.util.concurrent.ConcurrentHashMap;
  */
 public class DubboSpringInitializer {
 
-    private static Map<BeanDefinitionRegistry, DubboSpringInitializationContext> contextMap = new ConcurrentHashMap<>();
+    private static Map<BeanDefinitionRegistry, DubboSpringInitContext> contextMap = new ConcurrentHashMap<>();
 
     private DubboSpringInitializer() {
     }
 
     public static void initialize(BeanDefinitionRegistry registry) {
 
-        if (contextMap.putIfAbsent(registry, new DubboSpringInitializationContext()) != null) {
+        if (contextMap.putIfAbsent(registry, new DubboSpringInitContext()) != null) {
             return;
         }
 
         // prepare context and do customize
-        DubboSpringInitializationContext context = contextMap.get(registry);
+        DubboSpringInitContext context = contextMap.get(registry);
 
-        // find beanFactory and applicationContext
+        // find beanFactory
         ConfigurableListableBeanFactory beanFactory = findBeanFactory(registry);
-        ApplicationContext applicationContext = findApplicationContext(registry, beanFactory);
 
         // init dubbo context
-        initContext(context, registry, beanFactory, applicationContext);
+        initContext(context, registry, beanFactory);
     }
 
 
-    private static void initContext(DubboSpringInitializationContext context, BeanDefinitionRegistry registry,
-                                    ConfigurableListableBeanFactory beanFactory, ApplicationContext applicationContext) {
+    private static void initContext(DubboSpringInitContext context, BeanDefinitionRegistry registry,
+                                    ConfigurableListableBeanFactory beanFactory) {
         context.setRegistry(registry);
-        context.setApplicationContext(applicationContext);
+        context.setBeanFactory(beanFactory);
 
-        // customize context, you can change the bind module model via DubboSpringInitializationCustomizer SPI
+        // customize context, you can change the bind module model via DubboSpringInitCustomizer SPI
         customize(context);
 
         // init ApplicationModel
@@ -85,22 +79,9 @@ public class DubboSpringInitializer {
             context.setModuleModel(moduleModel);
         }
 
-        // Init SpringExtensionInjector
-        // Maybe the applicationContext is null, that means the Spring ApplicationContext is not completely created, so can not retrieve bean from it.
-        // We will reinitialize it again in DubboInfraBeanRegisterPostProcessor
-        if (applicationContext != null) {
-            SpringExtensionInjector.get(context.getApplicationModel()).init(applicationContext);
-        }
-
-        // create DubboBootstrap
-        DubboBootstrap bootstrap = context.getDubboBootstrap();
-        if (bootstrap == null) {
-            if (applicationModel == ApplicationModel.defaultModel()) {
-                bootstrap = DubboBootstrap.getInstance();
-            } else {
-                bootstrap = DubboBootstrap.newInstance(applicationModel);
-            }
-            context.setDubboBootstrap(bootstrap);
+        // set module attributes
+        if (context.getModuleAttributes().size() > 0) {
+            context.getModuleModel().getAttributes().putAll(context.getModuleAttributes());
         }
 
         // bind dubbo initialization context to spring context
@@ -126,31 +107,19 @@ public class DubboSpringInitializer {
         return beanFactory;
     }
 
-    private static ApplicationContext findApplicationContext(BeanDefinitionRegistry registry, ConfigurableListableBeanFactory beanFactory) {
-        // GenericApplicationContext
-        if (registry instanceof ApplicationContext) {
-            return (ApplicationContext) registry;
-        }
-        // find by ApplicationContextAware
-        ApplicationContextAwareBean contextBean = new ApplicationContextAwareBean();
-        beanFactory.initializeBean(contextBean, ApplicationContextAwareBean.class.getSimpleName());
-        return contextBean.applicationContext;
-    }
-
-    private static void registerContextBeans(ConfigurableListableBeanFactory beanFactory, DubboSpringInitializationContext context) {
+    private static void registerContextBeans(ConfigurableListableBeanFactory beanFactory, DubboSpringInitContext context) {
         // register singleton
         registerSingleton(beanFactory, context);
         registerSingleton(beanFactory, context.getApplicationModel());
         registerSingleton(beanFactory, context.getModuleModel());
-        registerSingleton(beanFactory, context.getDubboBootstrap());
     }
 
     private static void registerSingleton(ConfigurableListableBeanFactory beanFactory, Object bean) {
         beanFactory.registerSingleton(bean.getClass().getName(), bean);
     }
 
-    private static DubboSpringInitializationContext findContextForApplication(ApplicationModel applicationModel) {
-        for (DubboSpringInitializationContext initializationContext : contextMap.values()) {
+    private static DubboSpringInitContext findContextForApplication(ApplicationModel applicationModel) {
+        for (DubboSpringInitContext initializationContext : contextMap.values()) {
             if (initializationContext.getApplicationModel() == applicationModel) {
                 return initializationContext;
             }
@@ -158,25 +127,24 @@ public class DubboSpringInitializer {
         return null;
     }
 
-    private static void customize(DubboSpringInitializationContext context) {
+    private static void customize(DubboSpringInitContext context) {
 
         // find initialization customizers
-        Set<DubboSpringInitializationCustomizer> customizers = FrameworkModel.defaultModel()
-            .getExtensionLoader(DubboSpringInitializationCustomizer.class)
+        Set<DubboSpringInitCustomizer> customizers = FrameworkModel.defaultModel()
+            .getExtensionLoader(DubboSpringInitCustomizer.class)
             .getSupportedExtensionInstances();
-
-        for (DubboSpringInitializationCustomizer customizer : customizers) {
+        for (DubboSpringInitCustomizer customizer : customizers) {
             customizer.customize(context);
         }
-    }
-
-    static class ApplicationContextAwareBean implements ApplicationContextAware {
-
-        private ApplicationContext applicationContext;
 
-        @Override
-        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-            this.applicationContext = applicationContext;
+        // load customizers in thread local holder
+        DubboSpringInitCustomizerHolder customizerHolder = DubboSpringInitCustomizerHolder.get();
+        customizers = customizerHolder.getCustomizers();
+        for (DubboSpringInitCustomizer customizer : customizers) {
+            customizer.customize(context);
         }
+        customizerHolder.clearCustomizers();
+
     }
+
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java
index c8e51b8..9fcc5ed 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java
@@ -39,7 +39,7 @@ public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRe
         // initialize dubbo beans
         DubboSpringInitializer.initialize(registry);
 
-        // Config beans creating from props have move to DubboBootstrap
+        // Config beans creating from props have move to ConfigManager
 //        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
 //                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
 //
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboAnnotationInitedEvent.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboApplicationStateEvent.java
similarity index 51%
copy from dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboAnnotationInitedEvent.java
copy to dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboApplicationStateEvent.java
index b53e730..7b1c3f0 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboAnnotationInitedEvent.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboApplicationStateEvent.java
@@ -16,31 +16,39 @@
  */
 package org.apache.dubbo.config.spring.context.event;
 
-import org.apache.dubbo.config.spring.context.DubboConfigBeanInitializer;
-import org.springframework.context.ApplicationContext;
+import org.apache.dubbo.common.deploy.DeployState;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.springframework.context.ApplicationEvent;
 
 /**
- * An {@link ApplicationEvent} after Dubbo service/reference annotation has been processed.
- * <p />
- * NOTE: This event is used to trigger init {@link DubboConfigBeanInitializer}
+ * Dubbo application state event on starting/started/stopping/stopped
  */
-public class DubboAnnotationInitedEvent extends ApplicationEvent {
-    /**
-     * Create a new {@code ApplicationEvent}.
-     *
-     * @param source the object on which the event initially occurred or with
-     *               which the event is associated (never {@code null})
-     */
-    public DubboAnnotationInitedEvent(ApplicationContext source) {
-        super(source);
+public class DubboApplicationStateEvent extends ApplicationEvent {
+
+    private DeployState state;
+
+    private Throwable cause;
+
+    public DubboApplicationStateEvent(ApplicationModel applicationModel, DeployState state) {
+        super(applicationModel);
+        this.state = state;
+    }
+
+    public DubboApplicationStateEvent(ApplicationModel applicationModel, DeployState state, Throwable cause) {
+        super(applicationModel);
+        this.state = state;
+        this.cause = cause;
+    }
+
+    public ApplicationModel getApplicationModel() {
+        return (ApplicationModel) getSource();
     }
 
-    /**
-     * Get the {@code ApplicationContext} that the event was raised for.
-     */
-    public final ApplicationContext getApplicationContext() {
-        return (ApplicationContext) getSource();
+    public DeployState getState() {
+        return state;
     }
 
+    public Throwable getCause() {
+        return cause;
+    }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java
index 11b0e2e..21d245d 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStatedEvent.java
@@ -17,7 +17,6 @@
 package org.apache.dubbo.config.spring.context.event;
 
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-
 import org.springframework.context.ApplicationEvent;
 
 /**
@@ -28,6 +27,7 @@ import org.springframework.context.ApplicationEvent;
  * @see org.apache.dubbo.config.bootstrap.DubboBootstrap
  * @since 2.7.9
  */
+@Deprecated
 public class DubboBootstrapStatedEvent extends ApplicationEvent {
 
     /**
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java
index 0f5b5bf..90bf810 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboBootstrapStopedEvent.java
@@ -17,7 +17,6 @@
 package org.apache.dubbo.config.spring.context.event;
 
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-
 import org.springframework.context.ApplicationEvent;
 
 /**
@@ -28,6 +27,7 @@ import org.springframework.context.ApplicationEvent;
  * @see org.apache.dubbo.config.bootstrap.DubboBootstrap
  * @since 2.7.9
  */
+@Deprecated
 public class DubboBootstrapStopedEvent extends ApplicationEvent {
 
     /**
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboAnnotationInitedEvent.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboConfigInitEvent.java
similarity index 82%
rename from dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboAnnotationInitedEvent.java
rename to dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboConfigInitEvent.java
index b53e730..bf3bb3d 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboAnnotationInitedEvent.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/event/DubboConfigInitEvent.java
@@ -21,18 +21,17 @@ import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationEvent;
 
 /**
- * An {@link ApplicationEvent} after Dubbo service/reference annotation has been processed.
- * <p />
- * NOTE: This event is used to trigger init {@link DubboConfigBeanInitializer}
+ * An {@link ApplicationEvent} to trigger init {@link DubboConfigBeanInitializer}.
+ *
  */
-public class DubboAnnotationInitedEvent extends ApplicationEvent {
+public class DubboConfigInitEvent extends ApplicationEvent {
     /**
      * Create a new {@code ApplicationEvent}.
      *
      * @param source the object on which the event initially occurred or with
      *               which the event is associated (never {@code null})
      */
-    public DubboAnnotationInitedEvent(ApplicationContext source) {
+    public DubboConfigInitEvent(ApplicationContext source) {
         super(source);
     }
 
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanManager.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanManager.java
index ce5d42b..0c7619b7 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanManager.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/reference/ReferenceBeanManager.java
@@ -20,10 +20,9 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.dubbo.common.utils.Assert;
 import org.apache.dubbo.config.ReferenceConfig;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.spring.ReferenceBean;
-import org.apache.dubbo.config.spring.context.DubboSpringInitializationContext;
 import org.apache.dubbo.config.spring.util.DubboBeanUtils;
+import org.apache.dubbo.rpc.model.ModuleModel;
 import org.springframework.beans.BeansException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
@@ -54,9 +53,8 @@ public class ReferenceBeanManager implements ApplicationContextAware {
     private Map<String, ReferenceConfig> referenceConfigMap = new ConcurrentHashMap<>();
 
     private ApplicationContext applicationContext;
-    private DubboBootstrap dubboBootstrap;
     private volatile boolean initialized = false;
-    private DubboSpringInitializationContext initializationContext;
+    private ModuleModel moduleModel;
 
     public void addReference(ReferenceBean referenceBean) throws Exception {
         String referenceBeanName = referenceBean.getId();
@@ -119,8 +117,7 @@ public class ReferenceBeanManager implements ApplicationContextAware {
     @Override
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         this.applicationContext = applicationContext;
-        this.dubboBootstrap = DubboBeanUtils.getBootstrap(applicationContext);
-        initializationContext = DubboBeanUtils.getInitializationContext(applicationContext);
+        moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
     }
 
     /**
@@ -175,7 +172,7 @@ public class ReferenceBeanManager implements ApplicationContextAware {
             referenceConfigMap.put(referenceKey, referenceConfig);
 
             // register ReferenceConfig
-            dubboBootstrap.reference(referenceConfig, initializationContext.getModuleModel());
+            moduleModel.getConfigManager().addReference(referenceConfig);
         }
 
         // associate referenceConfig to referenceBean
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java
index 2cc878b..5fb6dd8 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/util/DubboBeanUtils.java
@@ -18,17 +18,19 @@ package org.apache.dubbo.config.spring.util;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.spring.beans.factory.annotation.DubboConfigAliasPostProcessor;
 import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor;
 import org.apache.dubbo.config.spring.beans.factory.annotation.ServicePackagesHolder;
 import org.apache.dubbo.config.spring.beans.factory.config.DubboConfigDefaultPropertyValueBeanPostProcessor;
 import org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener;
+import org.apache.dubbo.config.spring.context.DubboConfigApplicationListener;
 import org.apache.dubbo.config.spring.context.DubboConfigBeanInitializer;
+import org.apache.dubbo.config.spring.context.DubboDeployApplicationListener;
 import org.apache.dubbo.config.spring.context.DubboInfraBeanRegisterPostProcessor;
-import org.apache.dubbo.config.spring.context.DubboSpringInitializationContext;
+import org.apache.dubbo.config.spring.context.DubboSpringInitContext;
 import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
 import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
 import org.springframework.beans.factory.BeanFactory;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.config.BeanPostProcessor;
@@ -77,8 +79,12 @@ public interface DubboBeanUtils {
             DubboConfigAliasPostProcessor.class);
 
         // Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean
-        registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
-            DubboBootstrapApplicationListener.class);
+//        registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
+//            DubboBootstrapApplicationListener.class);
+
+        // register ApplicationListeners
+        registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
+        registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);
 
         // Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
         registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
@@ -178,26 +184,26 @@ public interface DubboBeanUtils {
         return getReferenceAnnotationBeanPostProcessor((AbstractBeanFactory) applicationContext.getAutowireCapableBeanFactory());
     }
 
-    static DubboSpringInitializationContext getInitializationContext(BeanFactory beanFactory) {
-        String beanName = DubboSpringInitializationContext.class.getName();
+    static DubboSpringInitContext getInitializationContext(BeanFactory beanFactory) {
+        String beanName = DubboSpringInitContext.class.getName();
         if (beanFactory.containsBean(beanName)) {
-            return beanFactory.getBean(beanName, DubboSpringInitializationContext.class);
+            return beanFactory.getBean(beanName, DubboSpringInitContext.class);
         }
         return null;
     }
 
-    static DubboBootstrap getBootstrap(BeanFactory beanFactory) {
-        String beanName = DubboBootstrap.class.getName();
+    static ApplicationModel getApplicationModel(BeanFactory beanFactory) {
+        String beanName = ApplicationModel.class.getName();
         if (beanFactory.containsBean(beanName)) {
-            return beanFactory.getBean(beanName, DubboBootstrap.class);
+            return beanFactory.getBean(beanName, ApplicationModel.class);
         }
         return null;
     }
 
-    static ApplicationModel getApplicationModel(BeanFactory beanFactory) {
-        String beanName = ApplicationModel.class.getName();
+    static ModuleModel getModuleModel(BeanFactory beanFactory) {
+        String beanName = ModuleModel.class.getName();
         if (beanFactory.containsBean(beanName)) {
-            return beanFactory.getBean(beanName, ApplicationModel.class);
+            return beanFactory.getBean(beanName, ModuleModel.class);
         }
         return null;
     }
diff --git a/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer
new file mode 100644
index 0000000..5f8a7e6
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer
@@ -0,0 +1 @@
+dubbo-config-spring=org.apache.dubbo.config.spring.SpringScopeModelInitializer
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java
index 5fd0ad6..fded00b 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ConfigTest.java
@@ -535,17 +535,25 @@ public class ConfigTest {
     // DUBBO-571 methods key in provider's URLONE doesn't contain the methods from inherited super interface
     @Test
     public void test_noMethodInterface_methodsKeyHasValue() throws Exception {
+        List<URL> urls = null;
         ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourcePath + "/demo-provider-no-methods-interface.xml");
         try {
             ctx.start();
+
             ServiceBean bean = (ServiceBean) ctx.getBean("service");
-            List<URL> urls = bean.getExportedUrls();
+            urls = bean.getExportedUrls();
             assertEquals(1, urls.size());
             URL url = urls.get(0);
             assertEquals("sayName,getBox", url.getParameter("methods"));
         } finally {
             ctx.stop();
             ctx.close();
+            // Check if the port is closed
+            if (urls != null) {
+                for (URL url : urls) {
+                    Assertions.assertFalse(NetUtils.isPortInUsed(url.getPort()));
+                }
+            }
         }
     }
 
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ShutdownHookTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ShutdownHookTest.java
index a0b1a74..9c98cc6 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ShutdownHookTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/ShutdownHookTest.java
@@ -16,8 +16,9 @@
  */
 package org.apache.dubbo.config.spring;
 
-import org.apache.dubbo.config.ConfigKeys;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.config.spring.context.DubboSpringInitCustomizerHolder;
+import org.apache.dubbo.rpc.model.ModuleModel;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -26,7 +27,11 @@ public class ShutdownHookTest {
 
     @Test
     public void testDisableShutdownHook(){
-        SysProps.setProperty(ConfigKeys.DUBBO_LIFECYCLE_DISABLE_SHUTDOWN_HOOK, "true");
+
+        // set KeepRunningOnSpringClosed flag for next spring context
+        DubboSpringInitCustomizerHolder.get().addCustomizer(context-> {
+            context.setKeepRunningOnSpringClosed(true);
+        });
 
         try {
             ClassPathXmlApplicationContext providerContext;
@@ -36,15 +41,17 @@ public class ShutdownHookTest {
                 resourcePath + "/demo-provider-properties.xml");
             providerContext.start();
 
+            ModuleModel moduleModel = providerContext.getBean(ModuleModel.class);
+            Assertions.assertTrue(moduleModel.getDeployer().isStarted());
             Assertions.assertEquals(true, DubboBootstrap.getInstance().isStarted());
-            Assertions.assertEquals(false, DubboBootstrap.getInstance().isShutdown());
+            Assertions.assertEquals(false, DubboBootstrap.getInstance().isStopped());
 
             // close spring context
             providerContext.close();
 
             // expect dubbo bootstrap will not be destroyed after closing spring context
             Assertions.assertEquals(true, DubboBootstrap.getInstance().isStarted());
-            Assertions.assertEquals(false, DubboBootstrap.getInstance().isShutdown());
+            Assertions.assertEquals(false, DubboBootstrap.getInstance().isStopped());
         } finally {
             SysProps.clear();
         }
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MethodConfigCallbackTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MethodConfigCallbackTest.java
index 0290f42..ce0b78c 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MethodConfigCallbackTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/MethodConfigCallbackTest.java
@@ -91,7 +91,7 @@ public class MethodConfigCallbackTest {
 
     @Test
     public void testMethodAnnotationCallBack() {
-        int threadCnt = Runtime.getRuntime().availableProcessors();
+        int threadCnt = Math.min(4, Runtime.getRuntime().availableProcessors());
         int callCnt = 2 * threadCnt;
         for (int i = 0; i < threadCnt; i++) {
             new Thread(() -> {
@@ -102,7 +102,7 @@ public class MethodConfigCallbackTest {
             }).start();
         }
         int i = 0;
-        while (MethodCallbackImpl.cnt.get() < ( 2 * threadCnt * callCnt) && i < 50){
+        while (MethodCallbackImpl.cnt.get() < ( 2 * threadCnt * callCnt)){
             // wait for async callback finished
             try {
                 i++;
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/DubboSpringInitCustomizerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/DubboSpringInitCustomizerTest.java
new file mode 100644
index 0000000..1b5a0f9
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/DubboSpringInitCustomizerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.spring.context.customize;
+
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.config.spring.SysProps;
+import org.apache.dubbo.config.spring.api.HelloService;
+import org.apache.dubbo.config.spring.context.DubboSpringInitCustomizerHolder;
+import org.apache.dubbo.config.spring.registrycenter.RegistryCenter;
+import org.apache.dubbo.config.spring.registrycenter.ZookeeperSingleRegistryCenter;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class DubboSpringInitCustomizerTest {
+
+    private static RegistryCenter singleRegistryCenter;
+
+    @BeforeAll
+    public static void beforeAll() {
+        DubboBootstrap.reset();
+        singleRegistryCenter = new ZookeeperSingleRegistryCenter();
+        singleRegistryCenter.startup();
+        RegistryCenter.Instance registryCenterInstance = singleRegistryCenter.getRegistryCenterInstance().get(0);
+        SysProps.setProperty("dubbo.registry.address", registryCenterInstance.toURL());
+
+    }
+
+    @AfterAll
+    public static void afterAll() {
+        DubboBootstrap.reset();
+        singleRegistryCenter.shutdown();
+        SysProps.clear();
+    }
+
+    @Test
+    public void testReloadSpringContext() {
+
+        ClassPathXmlApplicationContext providerContext1 = null;
+        ClassPathXmlApplicationContext providerContext2 = null;
+
+        ApplicationModel applicationModel = new FrameworkModel().newApplication();
+        applicationModel.getDefaultModule();
+
+        try {
+            // start spring context 1
+            ModuleModel moduleModel1 = applicationModel.newModule();
+            DubboSpringInitCustomizerHolder.get().addCustomizer(context -> {
+                context.setModuleModel(moduleModel1);
+            });
+
+            providerContext1 = new ClassPathXmlApplicationContext("dubbo-provider-v1.xml", getClass());
+            ModuleModel moduleModelFromSpring1 = providerContext1.getBean(ModuleModel.class);
+            Assertions.assertSame(moduleModel1, moduleModelFromSpring1);
+            String serviceKey1 = HelloService.class.getName() + ":1.0.0";
+            ServiceDescriptor serviceDescriptor1 = moduleModelFromSpring1.getServiceRepository().lookupService(serviceKey1);
+            Assertions.assertNotNull(serviceDescriptor1);
+
+            // close spring context 1
+            providerContext1.close();
+            Assertions.assertTrue(moduleModel1.isDestroyed());
+            Assertions.assertFalse(moduleModel1.getApplicationModel().isDestroyed());
+            providerContext1 = null;
+
+            ModuleModel moduleModel2 = applicationModel.newModule();
+            DubboSpringInitCustomizerHolder.get().addCustomizer(context -> {
+                context.setModuleModel(moduleModel2);
+            });
+
+            // load spring context 2
+            providerContext2 = new ClassPathXmlApplicationContext("dubbo-provider-v2.xml", getClass());
+            ModuleModel moduleModelFromSpring2 = providerContext2.getBean(ModuleModel.class);
+            Assertions.assertSame(moduleModel2, moduleModelFromSpring2);
+            Assertions.assertNotSame(moduleModelFromSpring1, moduleModelFromSpring2);
+            String serviceKey2 = HelloService.class.getName() + ":2.0.0";
+            ServiceDescriptor serviceDescriptor2 = moduleModelFromSpring2.getServiceRepository().lookupService(serviceKey2);
+            Assertions.assertNotNull(serviceDescriptor2);
+            Assertions.assertNotSame(serviceDescriptor1, serviceDescriptor2);
+
+            providerContext2.close();
+            providerContext2 = null;
+        }finally {
+            if (providerContext1 != null) {
+                providerContext1.close();
+            }
+            if (providerContext2 != null) {
+                providerContext2.close();
+            }
+            applicationModel.destroy();
+        }
+    }
+
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/dubbo-provider-v1.xml b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/dubbo-provider-v1.xml
new file mode 100644
index 0000000..53afe46
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/dubbo-provider-v1.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
+
+    <dubbo:application name="demo-provider">
+    </dubbo:application>
+
+    <dubbo:registry address="${dubbo.registry.address}"/>
+
+    <dubbo:protocol name="dubbo" port="-1"/>
+
+    <bean id="helloServiceImpl" class="org.apache.dubbo.config.spring.impl.HelloServiceImpl"/>
+
+    <dubbo:service interface="org.apache.dubbo.config.spring.api.HelloService" timeout="3000"
+                   version="1.0.0" ref="helloServiceImpl" protocol="dubbo"/>
+
+</beans>
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/dubbo-provider-v2.xml b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/dubbo-provider-v2.xml
new file mode 100644
index 0000000..de859f4
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/customize/dubbo-provider-v2.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
+
+    <dubbo:application name="demo-provider">
+    </dubbo:application>
+
+    <dubbo:registry address="${dubbo.registry.address}"/>
+
+    <dubbo:protocol name="dubbo" port="-1"/>
+
+    <bean id="helloServiceImpl" class="org.apache.dubbo.config.spring.context.annotation.provider.DefaultHelloService"/>
+
+    <dubbo:service interface="org.apache.dubbo.config.spring.api.HelloService" timeout="3000"
+                   version="2.0.0" ref="helloServiceImpl" protocol="dubbo"/>
+
+</beans>
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionInjectorTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionInjectorTest.java
index 2589b1d..78bf9eb 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionInjectorTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionInjectorTest.java
@@ -16,10 +16,11 @@
  */
 package org.apache.dubbo.config.spring.extension;
 
+import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.config.spring.api.DemoService;
 import org.apache.dubbo.config.spring.api.HelloService;
-import org.apache.dubbo.config.spring.context.DubboSpringInitializer;
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
 import org.apache.dubbo.config.spring.impl.DemoServiceImpl;
 import org.apache.dubbo.config.spring.impl.HelloServiceImpl;
 import org.apache.dubbo.config.spring.util.DubboBeanUtils;
@@ -32,6 +33,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+@EnableDubbo(scanBasePackages = "")
 @Configuration
 public class SpringExtensionInjectorTest {
 
@@ -53,9 +55,6 @@ public class SpringExtensionInjectorTest {
             context.register(getClass());
             context.refresh();
 
-            // mock dubbo spring initialize
-            DubboSpringInitializer.initialize(context);
-
             SpringExtensionInjector springExtensionInjector = SpringExtensionInjector.get(DubboBeanUtils.getApplicationModel(context));
             Protocol protocol = springExtensionInjector.getInstance(Protocol.class, "protocol");
             Assertions.assertNull(protocol);
@@ -83,4 +82,9 @@ public class SpringExtensionInjectorTest {
     public HelloService helloService() {
         return new HelloServiceImpl();
     }
+
+    @Bean
+    public ApplicationConfig applicationConfig() {
+        return new ApplicationConfig("test-app");
+    }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/DubboConfigBeanInitializerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/DubboConfigBeanInitializerTest.java
index 4748d63..63bdde2 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/DubboConfigBeanInitializerTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/reference/DubboConfigBeanInitializerTest.java
@@ -19,14 +19,15 @@ package org.apache.dubbo.config.spring.reference;
 
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.config.spring.registrycenter.ZookeeperSingleRegistryCenter;
-import org.apache.dubbo.config.spring.registrycenter.RegistryCenter;
 import org.apache.dubbo.config.spring.api.HelloService;
 import org.apache.dubbo.config.spring.context.DubboConfigBeanInitializer;
 import org.apache.dubbo.config.spring.context.annotation.provider.ProviderConfiguration;
+import org.apache.dubbo.config.spring.registrycenter.RegistryCenter;
+import org.apache.dubbo.config.spring.registrycenter.ZookeeperSingleRegistryCenter;
+import org.apache.dubbo.config.spring.util.DubboBeanUtils;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -108,8 +109,10 @@ public class DubboConfigBeanInitializerTest {
     @Configuration
     static class BusinessConfig {
         @Bean
-        public FooService fooService() {
-            // DubboBootstrap should be inited at DubboConfigInitializer, before init FooService bean
+        public FooService fooService(ApplicationContext applicationContext) {
+            // Dubbo config beans should be inited at DubboConfigInitializer, before init FooService bean
+            Assertions.assertTrue(DubboBeanUtils.getModuleModel(applicationContext).getDeployer().isInitialized());
+            Assertions.assertTrue(DubboBeanUtils.getApplicationModel(applicationContext).getDeployer().isInitialized());
             Assertions.assertTrue(DubboBootstrap.getInstance().isInitialized());
             return new FooService();
         }
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registrycenter/RegistryCenter.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registrycenter/RegistryCenter.java
index c3b1d71..0b8dd5b 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registrycenter/RegistryCenter.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registrycenter/RegistryCenter.java
@@ -66,5 +66,10 @@ public interface RegistryCenter {
          * Returns the port of registry center.
          */
         int getPort();
+
+        /**
+         * Return registry center url, such as: zookeeper://127.0.0.1:2181
+         */
+        String toURL();
     }
 }
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registrycenter/ZookeeperRegistryCenter.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registrycenter/ZookeeperRegistryCenter.java
index 72ab13a..0dd47a8 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registrycenter/ZookeeperRegistryCenter.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registrycenter/ZookeeperRegistryCenter.java
@@ -104,6 +104,11 @@ class ZookeeperRegistryCenter extends AbstractRegistryCenter {
                 public int getPort() {
                     return instanceSpec.getPort();
                 }
+
+                @Override
+                public String toURL() {
+                    return String.format("%s://%s:%d", getType(), getHostname(), getPort());
+                }
             });
         }
         return instances;
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
index 9484ebd..d899956 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/report/MetadataReportInstance.java
@@ -89,8 +89,4 @@ public class MetadataReportInstance {
         }
     }
 
-    public static void reset() {
-//        metadataReports.clear();
-//        init.set(false);
-    }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java
index d0864b9..2fc00e7 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ChangeTelnet.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import io.netty.channel.Channel;
+import io.netty.util.AttributeKey;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.qos.command.annotation.Cmd;
@@ -23,9 +25,6 @@ import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
 
-import io.netty.channel.Channel;
-import io.netty.util.AttributeKey;
-
 @Cmd(name = "cd", summary = "Change default service.", example = {
     "cd [service]"
 })
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
index 9bf0706..bd6a6f6 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/CountTelnet.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.qos.command.BaseCommand;
@@ -30,9 +32,6 @@ import org.apache.dubbo.rpc.RpcStatus;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
 
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelFuture;
-
 import java.lang.reflect.Method;
 import java.net.InetSocketAddress;
 import java.util.ArrayList;
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java
index f8f3a09..4f6dfee 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/InvokeTelnet.java
@@ -16,6 +16,9 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import com.alibaba.fastjson.JSON;
+import io.netty.channel.Channel;
+import io.netty.util.AttributeKey;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.ReflectUtils;
 import org.apache.dubbo.common.utils.StringUtils;
@@ -27,10 +30,6 @@ import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.model.MethodDescriptor;
 import org.apache.dubbo.rpc.model.ProviderModel;
 
-import com.alibaba.fastjson.JSON;
-import io.netty.channel.Channel;
-import io.netty.util.AttributeKey;
-
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PublishMetadata.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PublishMetadata.java
index 7deb58c..97767b1 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PublishMetadata.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/PublishMetadata.java
@@ -20,7 +20,7 @@ import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
 import org.apache.dubbo.common.utils.ArrayUtils;
-import org.apache.dubbo.config.bootstrap.DefaultApplicationDeployer;
+import org.apache.dubbo.config.deploy.DefaultApplicationDeployer;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.qos.command.annotation.Cmd;
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
index a2a75c2..c405eb8 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/SelectTelnet.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import io.netty.channel.Channel;
+import io.netty.util.AttributeKey;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.qos.command.BaseCommand;
@@ -23,9 +25,6 @@ import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.qos.command.annotation.Cmd;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 
-import io.netty.channel.Channel;
-import io.netty.util.AttributeKey;
-
 import java.lang.reflect.Method;
 import java.util.List;
 
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java
index 384a739..736e329 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/ShutdownTelnet.java
@@ -17,7 +17,7 @@
 package org.apache.dubbo.qos.command.impl;
 
 import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.config.bootstrap.DefaultApplicationDeployer;
+import org.apache.dubbo.config.deploy.DefaultApplicationDeployer;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.qos.command.annotation.Cmd;
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapReadinessProbe.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/DeployerReadinessProbe.java
similarity index 64%
rename from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapReadinessProbe.java
rename to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/DeployerReadinessProbe.java
index edf8f60..c2bfadf 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapReadinessProbe.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/DeployerReadinessProbe.java
@@ -17,13 +17,28 @@
 package org.apache.dubbo.qos.probe.impl;
 
 import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.qos.probe.ReadinessProbe;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
 
 @Activate
-public class BootstrapReadinessProbe implements ReadinessProbe {
+public class DeployerReadinessProbe implements ReadinessProbe {
+
+
+    private ApplicationModel applicationModel;
+
+    public DeployerReadinessProbe(ApplicationModel applicationModel) {
+        this.applicationModel = applicationModel;
+    }
+
     @Override
     public boolean check() {
-        return !DubboBootstrap.getInstance().isShutdown() && DubboBootstrap.getInstance().isStartup();
+        for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
+            if (!moduleModel.getDeployer().isStarted()) {
+                return false;
+            }
+        }
+        return true;
     }
+
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/DeployerStartupProbe.java
similarity index 64%
rename from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java
rename to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/DeployerStartupProbe.java
index 74105e7..532b9ba 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/DeployerStartupProbe.java
@@ -17,14 +17,26 @@
 package org.apache.dubbo.qos.probe.impl;
 
 import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.qos.probe.StartupProbe;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
 
 @Activate
-public class BootstrapStartupProbe implements StartupProbe {
+public class DeployerStartupProbe implements StartupProbe {
+
+    private ApplicationModel applicationModel;
+
+    public DeployerStartupProbe(ApplicationModel applicationModel) {
+        this.applicationModel = applicationModel;
+    }
 
     @Override
     public boolean check() {
-        return DubboBootstrap.getInstance().isStartup();
+        for (ModuleModel moduleModel : applicationModel.getModuleModels()) {
+            if (moduleModel.getDeployer().isRunning()) {
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java
index fb791b8..0eb6f79 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java
@@ -27,6 +27,8 @@ import java.util.List;
 
 @Activate
 public class ProviderReadinessProbe implements ReadinessProbe {
+
+
     private static FrameworkServiceRepository serviceRepository = FrameworkModel.defaultModel().getServiceRepository();
 
     @Override
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/Server.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/Server.java
index 2689ecd..5b51023 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/Server.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/Server.java
@@ -16,12 +16,6 @@
  */
 package org.apache.dubbo.qos.server;
 
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.StringUtils;
-import org.apache.dubbo.qos.server.handler.QosProcessHandler;
-import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelInitializer;
@@ -30,6 +24,11 @@ import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
 import io.netty.util.concurrent.DefaultThreadFactory;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.qos.server.handler.QosProcessHandler;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java
index 48aad6a..e8fc6e1 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java
@@ -16,15 +16,6 @@
  */
 package org.apache.dubbo.qos.server.handler;
 
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.CommandExecutor;
-import org.apache.dubbo.qos.command.DefaultCommandExecutor;
-import org.apache.dubbo.qos.command.NoSuchCommandException;
-import org.apache.dubbo.qos.command.decoder.HttpCommandDecoder;
-import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
@@ -36,6 +27,14 @@ import io.netty.handler.codec.http.HttpHeaders;
 import io.netty.handler.codec.http.HttpRequest;
 import io.netty.handler.codec.http.HttpResponseStatus;
 import io.netty.handler.codec.http.HttpVersion;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.CommandExecutor;
+import org.apache.dubbo.qos.command.DefaultCommandExecutor;
+import org.apache.dubbo.qos.command.NoSuchCommandException;
+import org.apache.dubbo.qos.command.decoder.HttpCommandDecoder;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 
 /**
  * Parse HttpRequest for uri and parameters
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java
index f1dc1ee..2bf5a16 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/QosProcessHandler.java
@@ -16,9 +16,6 @@
  */
 package org.apache.dubbo.qos.server.handler;
 
-import org.apache.dubbo.common.utils.ExecutorUtil;
-import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
@@ -33,6 +30,8 @@ import io.netty.handler.timeout.IdleStateEvent;
 import io.netty.handler.timeout.IdleStateHandler;
 import io.netty.util.CharsetUtil;
 import io.netty.util.concurrent.ScheduledFuture;
+import org.apache.dubbo.common.utils.ExecutorUtil;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 
 import java.util.List;
 import java.util.concurrent.TimeUnit;
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandler.java
index 4631401..f7560e5 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandler.java
@@ -16,6 +16,9 @@
  */
 package org.apache.dubbo.qos.server.handler;
 
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.StringUtils;
@@ -27,10 +30,6 @@ import org.apache.dubbo.qos.command.decoder.TelnetCommandDecoder;
 import org.apache.dubbo.qos.common.QosConstants;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 
-import io.netty.channel.ChannelFutureListener;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.SimpleChannelInboundHandler;
-
 /**
  * Telnet process handler
  */
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe
index 702ab02..a567102 100644
--- a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe
@@ -1,2 +1,2 @@
-bootstrap = org.apache.dubbo.qos.probe.impl.BootstrapReadinessProbe
-provider = org.apache.dubbo.qos.probe.impl.ProviderReadinessProbe
\ No newline at end of file
+deployer = org.apache.dubbo.qos.probe.impl.DeployerReadinessProbe
+provider = org.apache.dubbo.qos.probe.impl.ProviderReadinessProbe
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe
index 1095150..a213c98 100644
--- a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe
@@ -1 +1 @@
-bootstrap = org.apache.dubbo.qos.probe.impl.BootstrapStartupProbe
\ No newline at end of file
+bootstrap = org.apache.dubbo.qos.probe.impl.DeployerStartupProbe
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/DefaultCommandExecutorTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/DefaultCommandExecutorTest.java
index 8db17bf..af137f7 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/DefaultCommandExecutorTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/DefaultCommandExecutorTest.java
@@ -18,7 +18,6 @@
 package org.apache.dubbo.qos.command;
 
 import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ChangeTelnetTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ChangeTelnetTest.java
index cd0f6be..0b08b87 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ChangeTelnetTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ChangeTelnetTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import io.netty.channel.Channel;
+import io.netty.util.DefaultAttributeMap;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.qos.command.BaseCommand;
@@ -26,9 +28,6 @@ import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
-
-import io.netty.channel.Channel;
-import io.netty.util.DefaultAttributeMap;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/CountTelnetTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/CountTelnetTest.java
index 8f40b3f..1c0bfc6 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/CountTelnetTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/CountTelnetTest.java
@@ -29,7 +29,6 @@ import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.RpcStatus;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/HelpTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/HelpTest.java
index 642637d..17031fc 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/HelpTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/HelpTest.java
@@ -18,7 +18,6 @@ package org.apache.dubbo.qos.command.impl;
 
 import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/InvokeTelnetTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/InvokeTelnetTest.java
index e2e97ea..8fcda4c 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/InvokeTelnetTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/InvokeTelnetTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import io.netty.channel.Channel;
+import io.netty.util.DefaultAttributeMap;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
@@ -27,9 +29,6 @@ import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.model.ModuleServiceRepository;
 import org.apache.dubbo.rpc.model.ServiceDescriptor;
-
-import io.netty.channel.Channel;
-import io.netty.util.DefaultAttributeMap;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PortTelnetTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PortTelnetTest.java
index ed98538..2752935 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PortTelnetTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/PortTelnetTest.java
@@ -30,7 +30,6 @@ import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SelectTelnetTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SelectTelnetTest.java
index 1de057a..b66ba48 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SelectTelnetTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/SelectTelnetTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import io.netty.channel.Channel;
+import io.netty.util.DefaultAttributeMap;
 import org.apache.dubbo.config.bootstrap.DubboBootstrap;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
@@ -27,9 +29,6 @@ import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.model.ModuleServiceRepository;
 import org.apache.dubbo.rpc.model.ServiceDescriptor;
-
-import io.netty.channel.Channel;
-import io.netty.util.DefaultAttributeMap;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ShutdownTelnetTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ShutdownTelnetTest.java
index e9b208a..4e574a8 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ShutdownTelnetTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/impl/ShutdownTelnetTest.java
@@ -16,13 +16,12 @@
  */
 package org.apache.dubbo.qos.command.impl;
 
+import io.netty.channel.Channel;
 import org.apache.dubbo.qos.command.BaseCommand;
 import org.apache.dubbo.qos.command.CommandContext;
 import org.apache.dubbo.qos.legacy.ProtocolUtils;
 import org.apache.dubbo.remoting.RemotingException;
 import org.apache.dubbo.rpc.model.FrameworkModel;
-
-import io.netty.channel.Channel;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
index 328e8f9..377d4c3 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
@@ -39,7 +39,6 @@ import org.apache.dubbo.qos.command.impl.ShutdownTelnet;
 import org.apache.dubbo.qos.command.impl.Startup;
 import org.apache.dubbo.qos.command.impl.Version;
 import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import org.junit.jupiter.api.Test;
 
 import java.util.LinkedList;
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/protocol/QosProtocolWrapperTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/protocol/QosProtocolWrapperTest.java
index 1a45665..a278226 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/protocol/QosProtocolWrapperTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/protocol/QosProtocolWrapperTest.java
@@ -22,7 +22,6 @@ import org.apache.dubbo.qos.server.Server;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
 import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/HttpProcessHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/HttpProcessHandlerTest.java
index ba3f3cc..6cecfae 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/HttpProcessHandlerTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/HttpProcessHandlerTest.java
@@ -16,14 +16,13 @@
  */
 package org.apache.dubbo.qos.server.handler;
 
-import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.http.FullHttpResponse;
 import io.netty.handler.codec.http.HttpMethod;
 import io.netty.handler.codec.http.HttpRequest;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/QosProcessHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/QosProcessHandlerTest.java
index 271a277..523666a 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/QosProcessHandlerTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/QosProcessHandlerTest.java
@@ -16,8 +16,6 @@
  */
 package org.apache.dubbo.qos.server.handler;
 
-import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
 import io.netty.channel.ChannelHandlerContext;
@@ -27,6 +25,7 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
 import io.netty.handler.codec.http.HttpServerCodec;
 import io.netty.handler.codec.string.StringDecoder;
 import io.netty.handler.codec.string.StringEncoder;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandlerTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandlerTest.java
index 759d664..cc4c427 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandlerTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/server/handler/TelnetProcessHandlerTest.java
@@ -16,11 +16,10 @@
  */
 package org.apache.dubbo.qos.server.handler;
 
-import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
index 15a276d..21be5e1 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.registry.client.metadata;
 
+import com.google.gson.Gson;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.Logger;
@@ -32,8 +33,6 @@ import org.apache.dubbo.registry.client.ServiceInstanceCustomizer;
 import org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl;
 import org.apache.dubbo.registry.support.RegistryManager;
 
-import com.google.gson.Gson;
-
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/AbstractRegistryFactoryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/AbstractRegistryFactoryTest.java
index 6347b78..2ccd744 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/AbstractRegistryFactoryTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/AbstractRegistryFactoryTest.java
@@ -21,7 +21,6 @@ import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.rpc.model.ApplicationModel;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistryTest.java b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistryTest.java
index af9131d..23d5af9 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistryTest.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperRegistryTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.registry.zookeeper;
 
+import org.apache.curator.test.TestingServer;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.status.Status;
 import org.apache.dubbo.common.utils.NetUtils;
@@ -23,8 +24,6 @@ import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.registry.status.RegistryStatusChecker;
 import org.apache.dubbo.rpc.model.ApplicationModel;
-
-import org.apache.curator.test.TestingServer;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
index bb449b1..4361a7a 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/DubboProtocol.java
@@ -650,13 +650,13 @@ public class DubboProtocol extends AbstractProtocol {
 
             try {
                 if (logger.isInfoEnabled()) {
-                    logger.info("Close dubbo server: " + server.getLocalAddress());
+                    logger.info("Closing dubbo server: " + server.getLocalAddress());
                 }
 
                 server.close(ConfigurationUtils.getServerShutdownTimeout(server.getUrl().getScopeModel()));
 
             } catch (Throwable t) {
-                logger.warn(t.getMessage(), t);
+                logger.warn("Close dubbo server [" + server.getLocalAddress()+ "] failed: " + t.getMessage(), t);
             }
         }
 
diff --git a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ArgumentCallbackTest.java b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ArgumentCallbackTest.java
index ff7dd83..7063762 100644
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ArgumentCallbackTest.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/test/java/org/apache/dubbo/rpc/protocol/dubbo/ArgumentCallbackTest.java
@@ -26,7 +26,6 @@ import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ConsumerModel;
 import org.apache.dubbo.rpc.model.ModuleServiceRepository;
 import org.apache.dubbo.rpc.protocol.dubbo.support.ProtocolUtils;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractClientStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractClientStream.java
index d8156bd..b0898ba 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractClientStream.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractClientStream.java
@@ -17,6 +17,8 @@
 
 package org.apache.dubbo.rpc.protocol.tri;
 
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpHeaderValues;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.stream.StreamObserver;
@@ -25,9 +27,6 @@ import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.model.ConsumerModel;
 import org.apache.dubbo.triple.TripleWrapper;
 
-import io.netty.handler.codec.http.HttpHeaderNames;
-import io.netty.handler.codec.http.HttpHeaderValues;
-
 import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractServerStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractServerStream.java
index 9c8bd0e..016dd64 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractServerStream.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractServerStream.java
@@ -17,6 +17,7 @@
 
 package org.apache.dubbo.rpc.protocol.tri;
 
+import com.google.protobuf.Message;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
@@ -31,8 +32,6 @@ import org.apache.dubbo.rpc.model.ScopeModelUtil;
 import org.apache.dubbo.rpc.model.ServiceDescriptor;
 import org.apache.dubbo.triple.TripleWrapper;
 
-import com.google.protobuf.Message;
-
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractStream.java
index 4ea0d45..3b8f7fe 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractStream.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/AbstractStream.java
@@ -17,6 +17,10 @@
 
 package org.apache.dubbo.rpc.protocol.tri;
 
+import com.google.protobuf.Any;
+import com.google.rpc.DebugInfo;
+import com.google.rpc.Status;
+import io.netty.handler.codec.http2.Http2Headers;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.serialize.MultipleSerialization;
@@ -29,11 +33,6 @@ import org.apache.dubbo.rpc.model.MethodDescriptor;
 import org.apache.dubbo.rpc.model.ServiceDescriptor;
 import org.apache.dubbo.rpc.protocol.tri.GrpcStatus.Code;
 
-import com.google.protobuf.Any;
-import com.google.rpc.DebugInfo;
-import com.google.rpc.Status;
-import io.netty.handler.codec.http2.Http2Headers;
-
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcStatus.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcStatus.java
index c21ec32..0df9771 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcStatus.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/GrpcStatus.java
@@ -16,13 +16,12 @@
  */
 package org.apache.dubbo.rpc.protocol.tri;
 
+import io.netty.handler.codec.http.QueryStringDecoder;
+import io.netty.handler.codec.http.QueryStringEncoder;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.remoting.exchange.Response;
 import org.apache.dubbo.rpc.RpcException;
 
-import io.netty.handler.codec.http.QueryStringDecoder;
-import io.netty.handler.codec.http.QueryStringEncoder;
-
 import static org.apache.dubbo.rpc.RpcException.FORBIDDEN_EXCEPTION;
 import static org.apache.dubbo.rpc.RpcException.LIMIT_EXCEEDED_EXCEPTION;
 import static org.apache.dubbo.rpc.RpcException.METHOD_NOT_FOUND;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleClientHandler.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleClientHandler.java
index 0b9d45d..a8ccb01 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleClientHandler.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleClientHandler.java
@@ -16,6 +16,12 @@
  */
 package org.apache.dubbo.rpc.protocol.tri;
 
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.codec.http2.Http2GoAwayFrame;
+import io.netty.handler.codec.http2.Http2SettingsFrame;
+import io.netty.util.ReferenceCountUtil;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.stream.StreamObserver;
@@ -33,13 +39,6 @@ import org.apache.dubbo.rpc.model.ConsumerModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.model.MethodDescriptor;
 
-import io.netty.channel.ChannelDuplexHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPromise;
-import io.netty.handler.codec.http2.Http2GoAwayFrame;
-import io.netty.handler.codec.http2.Http2SettingsFrame;
-import io.netty.util.ReferenceCountUtil;
-
 import java.util.Arrays;
 import java.util.List;
 
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2FrameServerHandler.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2FrameServerHandler.java
index 1ad2b80..0c0daf9 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2FrameServerHandler.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2FrameServerHandler.java
@@ -16,20 +16,6 @@
  */
 package org.apache.dubbo.rpc.protocol.tri;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.constants.CommonConstants;
-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.rpc.Invoker;
-import org.apache.dubbo.rpc.RpcException;
-import org.apache.dubbo.rpc.model.FrameworkModel;
-import org.apache.dubbo.rpc.model.FrameworkServiceRepository;
-import org.apache.dubbo.rpc.model.MethodDescriptor;
-import org.apache.dubbo.rpc.model.ProviderModel;
-import org.apache.dubbo.rpc.protocol.tri.GrpcStatus.Code;
-import org.apache.dubbo.rpc.service.ServiceDescriptorInternalCache;
-
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelDuplexHandler;
 import io.netty.channel.ChannelHandlerContext;
@@ -43,6 +29,19 @@ import io.netty.handler.codec.http2.Http2Frame;
 import io.netty.handler.codec.http2.Http2Headers;
 import io.netty.handler.codec.http2.Http2HeadersFrame;
 import io.netty.util.ReferenceCountUtil;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+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.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.FrameworkServiceRepository;
+import org.apache.dubbo.rpc.model.MethodDescriptor;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.protocol.tri.GrpcStatus.Code;
+import org.apache.dubbo.rpc.service.ServiceDescriptorInternalCache;
 
 import java.util.List;
 
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java
index 625136b..79eba43 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleHttp2Protocol.java
@@ -16,14 +16,6 @@
  */
 package org.apache.dubbo.rpc.protocol.tri;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.config.Configuration;
-import org.apache.dubbo.common.config.ConfigurationUtils;
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.remoting.api.Http2WireProtocol;
-import org.apache.dubbo.rpc.model.FrameworkModel;
-import org.apache.dubbo.rpc.model.ScopeModelAware;
-
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelPipeline;
 import io.netty.channel.SimpleChannelInboundHandler;
@@ -32,6 +24,13 @@ import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
 import io.netty.handler.codec.http2.Http2MultiplexHandler;
 import io.netty.handler.codec.http2.Http2Settings;
 import io.netty.handler.ssl.SslContext;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.Configuration;
+import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.api.Http2WireProtocol;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ScopeModelAware;
 
 import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_ENABLE_PUSH_KEY;
 import static org.apache.dubbo.rpc.Constants.H2_SETTINGS_HEADER_TABLE_SIZE_KEY;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java
index 4df7370..f049f9d 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/TripleInvoker.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.rpc.protocol.tri;
 
+import io.netty.channel.ChannelFuture;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.Version;
 import org.apache.dubbo.common.utils.StringUtils;
@@ -40,8 +41,6 @@ import org.apache.dubbo.rpc.TimeoutCountDown;
 import org.apache.dubbo.rpc.protocol.AbstractInvoker;
 import org.apache.dubbo.rpc.support.RpcUtils;
 
-import io.netty.channel.ChannelFuture;
-
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryClientStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryClientStream.java
index 6ae3b59..415bdc5 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryClientStream.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryClientStream.java
@@ -17,6 +17,9 @@
 
 package org.apache.dubbo.rpc.protocol.tri;
 
+import com.google.protobuf.Any;
+import com.google.rpc.DebugInfo;
+import com.google.rpc.Status;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.stream.StreamObserver;
 import org.apache.dubbo.remoting.exchange.Response;
@@ -24,10 +27,6 @@ import org.apache.dubbo.remoting.exchange.support.DefaultFuture2;
 import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.RpcException;
 
-import com.google.protobuf.Any;
-import com.google.rpc.DebugInfo;
-import com.google.rpc.Status;
-
 import java.util.List;
 import java.util.Map;
 
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryServerStream.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryServerStream.java
index 2140463..27005a6 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryServerStream.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/UnaryServerStream.java
@@ -17,6 +17,7 @@
 
 package org.apache.dubbo.rpc.protocol.tri;
 
+import io.netty.handler.codec.http.HttpHeaderNames;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.stream.StreamObserver;
 import org.apache.dubbo.remoting.TimeoutException;
@@ -26,8 +27,6 @@ import org.apache.dubbo.rpc.RpcContext;
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.RpcInvocation;
 
-import io.netty.handler.codec.http.HttpHeaderNames;
-
 import java.util.Map;
 import java.util.concurrent.CompletionStage;
 import java.util.function.BiConsumer;
diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboShutdownMetadata.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboShutdownMetadata.java
index bdbd125..1ae860b 100644
--- a/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboShutdownMetadata.java
+++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/actuator/src/main/java/org/apache/dubbo/spring/boot/actuate/endpoint/metadata/DubboShutdownMetadata.java
@@ -20,7 +20,6 @@ import org.apache.dubbo.config.ReferenceConfigBase;
 import org.apache.dubbo.config.spring.ServiceBean;
 import org.apache.dubbo.registry.support.RegistryManager;
 import org.apache.dubbo.rpc.model.ApplicationModel;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
diff --git a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java
index c98c0bb..8dd4266 100644
--- a/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java
+++ b/dubbo-spring-boot/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java
@@ -16,7 +16,7 @@
  */
 package org.apache.dubbo.spring.boot.context.event;
 
-import org.apache.dubbo.config.DubboShutdownHook;
+import org.apache.dubbo.common.lang.ShutdownHookCallbacks;
 import org.apache.dubbo.config.spring.util.DubboBeanUtils;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.slf4j.Logger;
@@ -125,9 +125,9 @@ public class AwaitingNonWebApplicationListener implements SmartApplicationListen
         if (applicationModel == null) {
             return;
         }
-        DubboShutdownHook dubboShutdownHook = applicationModel.getBeanFactory().getBean(DubboShutdownHook.class);
-        if (dubboShutdownHook != null) {
-            dubboShutdownHook.addCallback(this::release);
+        ShutdownHookCallbacks shutdownHookCallbacks = applicationModel.getBeanFactory().getBean(ShutdownHookCallbacks.class);
+        if (shutdownHookCallbacks != null) {
+            shutdownHookCallbacks.addCallback(this::release);
         }
     }
 
diff --git a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringAnnotationBeanTest.java b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringAnnotationBeanTest.java
index e06b8f3..61376ce 100644
--- a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringAnnotationBeanTest.java
+++ b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringAnnotationBeanTest.java
@@ -22,10 +22,10 @@ import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
 import org.apache.dubbo.test.common.api.DemoService;
 import org.apache.dubbo.test.common.registrycenter.RegistryCenter;
 import org.apache.dubbo.test.common.registrycenter.ZookeeperSingleRegistryCenter;
-import org.apache.dubbo.test.spring.context.MockSpringInitializationCustomizer;
+import org.apache.dubbo.test.spring.context.MockSpringInitCustomizer;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Test;
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 import org.springframework.context.annotation.Bean;
@@ -56,7 +56,7 @@ public class SpringAnnotationBeanTest {
         testService.test();
 
         // check initialization customizer
-        MockSpringInitializationCustomizer.checkCustomizer(applicationContext);
+        MockSpringInitCustomizer.checkCustomizer(applicationContext);
     }
 
     @EnableDubbo(scanBasePackages = "org.apache.dubbo.test.common.impl")
diff --git a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringJavaConfigBeanTest.java b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringJavaConfigBeanTest.java
index 8379fda..31239f9 100644
--- a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringJavaConfigBeanTest.java
+++ b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringJavaConfigBeanTest.java
@@ -35,7 +35,7 @@ import org.apache.dubbo.test.common.api.DemoService;
 import org.apache.dubbo.test.common.impl.DemoServiceImpl;
 import org.apache.dubbo.test.common.registrycenter.RegistryCenter;
 import org.apache.dubbo.test.common.registrycenter.ZookeeperSingleRegistryCenter;
-import org.apache.dubbo.test.spring.context.MockSpringInitializationCustomizer;
+import org.apache.dubbo.test.spring.context.MockSpringInitCustomizer;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
@@ -135,7 +135,7 @@ public class SpringJavaConfigBeanTest {
             Assertions.assertEquals("Hello dubbo", result);
 
             // check initialization customizer
-            MockSpringInitializationCustomizer.checkCustomizer(consumerContext);
+            MockSpringInitCustomizer.checkCustomizer(consumerContext);
         } finally {
             consumerContext.close();
         }
diff --git a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringXmlConfigTest.java b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringXmlConfigTest.java
index ace14ec..7d7eba1 100644
--- a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringXmlConfigTest.java
+++ b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/SpringXmlConfigTest.java
@@ -23,7 +23,7 @@ import org.apache.dubbo.test.common.api.GreetingService;
 import org.apache.dubbo.test.common.api.RestDemoService;
 import org.apache.dubbo.test.common.registrycenter.RegistryCenter;
 import org.apache.dubbo.test.common.registrycenter.ZookeeperSingleRegistryCenter;
-import org.apache.dubbo.test.spring.context.MockSpringInitializationCustomizer;
+import org.apache.dubbo.test.spring.context.MockSpringInitCustomizer;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
@@ -69,7 +69,7 @@ public class SpringXmlConfigTest {
             Assertions.assertEquals("Hello, dubbo", resetHelloResult);
 
             // check initialization customizer
-            MockSpringInitializationCustomizer.checkCustomizer(applicationContext);
+            MockSpringInitCustomizer.checkCustomizer(applicationContext);
         } finally {
             SysProps.clear();
             if (applicationContext != null) {
diff --git a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/context/MockSpringInitializationCustomizer.java b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/context/MockSpringInitCustomizer.java
similarity index 74%
rename from dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/context/MockSpringInitializationCustomizer.java
rename to dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/context/MockSpringInitCustomizer.java
index 093f419..ac85443 100644
--- a/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/context/MockSpringInitializationCustomizer.java
+++ b/dubbo-test/dubbo-test-spring/src/main/java/org/apache/dubbo/test/spring/context/MockSpringInitCustomizer.java
@@ -17,8 +17,8 @@
 package org.apache.dubbo.test.spring.context;
 
 import org.apache.dubbo.common.extension.ExtensionLoader;
-import org.apache.dubbo.config.spring.context.DubboSpringInitializationContext;
-import org.apache.dubbo.config.spring.context.DubboSpringInitializationCustomizer;
+import org.apache.dubbo.config.spring.context.DubboSpringInitContext;
+import org.apache.dubbo.config.spring.context.DubboSpringInitCustomizer;
 import org.junit.jupiter.api.Assertions;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
@@ -31,12 +31,12 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
-public class MockSpringInitializationCustomizer implements DubboSpringInitializationCustomizer {
+public class MockSpringInitCustomizer implements DubboSpringInitCustomizer {
 
-    private List<DubboSpringInitializationContext> contexts = new ArrayList<>();
+    private List<DubboSpringInitContext> contexts = new ArrayList<>();
 
     @Override
-    public void customize(DubboSpringInitializationContext context) {
+    public void customize(DubboSpringInitContext context) {
         this.contexts.add(context);
 
         // register post-processor bean, expecting the bean is loaded and invoked by spring container
@@ -46,7 +46,7 @@ public class MockSpringInitializationCustomizer implements DubboSpringInitializa
         context.getRegistry().registerBeanDefinition(CustomBeanFactoryPostProcessor.class.getName(), beanDefinition);
     }
 
-    public List<DubboSpringInitializationContext> getContexts() {
+    public List<DubboSpringInitContext> getContexts() {
         return contexts;
     }
 
@@ -60,14 +60,14 @@ public class MockSpringInitializationCustomizer implements DubboSpringInitializa
     }
 
     public static void checkCustomizer(ConfigurableApplicationContext applicationContext) {
-        Set<DubboSpringInitializationCustomizer> customizers = ExtensionLoader
-            .getExtensionLoader(DubboSpringInitializationCustomizer.class)
+        Set<DubboSpringInitCustomizer> customizers = ExtensionLoader
+            .getExtensionLoader(DubboSpringInitCustomizer.class)
             .getSupportedExtensionInstances();
 
-        MockSpringInitializationCustomizer mockCustomizer = null;
-        for (DubboSpringInitializationCustomizer customizer : customizers) {
-            if (customizer instanceof MockSpringInitializationCustomizer) {
-                mockCustomizer = (MockSpringInitializationCustomizer) customizer;
+        MockSpringInitCustomizer mockCustomizer = null;
+        for (DubboSpringInitCustomizer customizer : customizers) {
+            if (customizer instanceof MockSpringInitCustomizer) {
+                mockCustomizer = (MockSpringInitCustomizer) customizer;
                 break;
             }
         }
@@ -75,8 +75,8 @@ public class MockSpringInitializationCustomizer implements DubboSpringInitializa
 
         // check applicationContext
         boolean foundInitContext = false;
-        List<DubboSpringInitializationContext> contexts = mockCustomizer.getContexts();
-        for (DubboSpringInitializationContext initializationContext : contexts) {
+        List<DubboSpringInitContext> contexts = mockCustomizer.getContexts();
+        for (DubboSpringInitContext initializationContext : contexts) {
             if (initializationContext.getRegistry() == applicationContext.getBeanFactory()) {
                 foundInitContext = true;
                 break;
diff --git a/dubbo-test/dubbo-test-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitCustomizer b/dubbo-test/dubbo-test-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitCustomizer
new file mode 100644
index 0000000..b2dd06c
--- /dev/null
+++ b/dubbo-test/dubbo-test-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitCustomizer
@@ -0,0 +1 @@
+mock=org.apache.dubbo.test.spring.context.MockSpringInitCustomizer
diff --git a/dubbo-test/dubbo-test-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitializationCustomizer b/dubbo-test/dubbo-test-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitializationCustomizer
deleted file mode 100644
index 5b63f73..0000000
--- a/dubbo-test/dubbo-test-spring/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.config.spring.context.DubboSpringInitializationCustomizer
+++ /dev/null
@@ -1 +0,0 @@
-mock=org.apache.dubbo.test.spring.context.MockSpringInitializationCustomizer