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

[dubbo] 13/14: Merge branch 'merge-3.x' into master-hsf

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

liujun pushed a commit to branch master-hsf
in repository https://gitbox.apache.org/repos/asf/dubbo.git

commit bfa059cd2b054caaaca34843074acaad17149d2e
Merge: 48ef064 a619be6
Author: ken.lj <ke...@gmail.com>
AuthorDate: Thu Dec 12 15:46:25 2019 +0800

    Merge branch 'merge-3.x' into master-hsf
    
    # Conflicts:
    #	dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
    #	dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerMethodModel.java
    #	dubbo-compatible/src/test/java/org/apache/dubbo/config/MethodConfigTest.java
    #	dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ReferenceConfig.java
    #	dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/context/ConfigManager.java
    #	dubbo-configcenter/dubbo-configcenter-api/src/main/java/org/apache/dubbo/configcenter/ConfigChangeEvent.java
    #	dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Offline.java
    #	dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/AsyncRpcResult.java
    #	dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/model/ConsumerModel.java
    #	dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java

 .../org/apache/dubbo/common/ServiceDescriptor.java | 16 ++--
 .../dubbo/common/extension/ExtensionLoader.java    | 45 ++++++++---
 .../dubbo/common/extension/LoadingStrategy.java    | 29 ++++++++
 .../org/apache/dubbo/config/AbstractConfig.java    |  7 +-
 .../dubbo/rpc/model/ConsumerMethodModel.java       | 48 +-----------
 .../org/apache/dubbo/config/MethodConfigTest.java  |  4 +-
 .../org/apache/dubbo/qos/command/impl/Offline.java |  2 +-
 .../org/apache/dubbo/qos/command/impl/Online.java  |  4 +-
 .../remoting/exchange/support/DefaultFuture.java   | 23 +++---
 .../apache/dubbo/rpc/model/AsyncMethodInfo.java    | 87 ++++++++++++++++++++++
 .../rpc/protocol/dubbo/filter/FutureFilter.java    | 16 ++--
 11 files changed, 186 insertions(+), 95 deletions(-)

diff --cc dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
index 0babf10,0000000..b0a9400
mode 100644,000000..100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java
@@@ -1,611 -1,0 +1,610 @@@
 +/*
 + * 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;
 +
 +import org.apache.dubbo.common.URL;
 +import org.apache.dubbo.common.config.CompositeConfiguration;
 +import org.apache.dubbo.common.config.Configuration;
 +import org.apache.dubbo.common.config.Environment;
 +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.ClassUtils;
 +import org.apache.dubbo.common.utils.CollectionUtils;
 +import org.apache.dubbo.common.utils.MethodUtils;
 +import org.apache.dubbo.common.utils.ReflectUtils;
 +import org.apache.dubbo.common.utils.StringUtils;
 +import org.apache.dubbo.config.context.ConfigConfigurationAdapter;
 +import org.apache.dubbo.config.context.ConfigManager;
 +import org.apache.dubbo.config.support.Parameter;
- import org.apache.dubbo.rpc.model.ApplicationModel;
- import org.apache.dubbo.rpc.model.ConsumerModel;
++import org.apache.dubbo.rpc.model.AsyncMethodInfo;
 +
 +import javax.annotation.PostConstruct;
 +import java.io.Serializable;
 +import java.lang.reflect.Method;
 +import java.lang.reflect.Modifier;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.Map;
 +import java.util.Objects;
 +import java.util.Set;
 +import java.util.concurrent.atomic.AtomicBoolean;
 +import java.util.stream.Collectors;
 +
 +import static org.apache.dubbo.common.utils.ReflectUtils.findMethodByMethodSignature;
 +
 +/**
 + * Utility methods and public methods for parsing configuration
 + *
 + * @export
 + */
 +public abstract class AbstractConfig implements Serializable {
 +
 +    protected static final Logger logger = LoggerFactory.getLogger(AbstractConfig.class);
 +    private static final long serialVersionUID = 4267533505537413570L;
 +
 +    /**
 +     * The legacy properties container
 +     */
 +    private static final Map<String, String> LEGACY_PROPERTIES = new HashMap<String, String>();
 +
 +    /**
 +     * The suffix container
 +     */
 +    private static final String[] SUFFIXES = new String[]{"Config", "Bean", "ConfigBase"};
 +
 +    static {
 +        LEGACY_PROPERTIES.put("dubbo.protocol.name", "dubbo.service.protocol");
 +        LEGACY_PROPERTIES.put("dubbo.protocol.host", "dubbo.service.server.host");
 +        LEGACY_PROPERTIES.put("dubbo.protocol.port", "dubbo.service.server.port");
 +        LEGACY_PROPERTIES.put("dubbo.protocol.threads", "dubbo.service.max.thread.pool.size");
 +        LEGACY_PROPERTIES.put("dubbo.consumer.timeout", "dubbo.service.invoke.timeout");
 +        LEGACY_PROPERTIES.put("dubbo.consumer.retries", "dubbo.service.max.retry.providers");
 +        LEGACY_PROPERTIES.put("dubbo.consumer.check", "dubbo.service.allow.no.provider");
 +        LEGACY_PROPERTIES.put("dubbo.service.url", "dubbo.service.address");
 +    }
 +
 +    /**
 +     * The config id
 +     */
 +    protected String id;
 +    protected String prefix;
 +
 +    protected final AtomicBoolean refreshed = new AtomicBoolean(false);
 +
 +    private static String convertLegacyValue(String key, String value) {
 +        if (value != null && value.length() > 0) {
 +            if ("dubbo.service.max.retry.providers".equals(key)) {
 +                return String.valueOf(Integer.parseInt(value) - 1);
 +            } else if ("dubbo.service.allow.no.provider".equals(key)) {
 +                return String.valueOf(!Boolean.parseBoolean(value));
 +            }
 +        }
 +        return value;
 +    }
 +
 +    public static String getTagName(Class<?> cls) {
 +        String tag = cls.getSimpleName();
 +        for (String suffix : SUFFIXES) {
 +            if (tag.endsWith(suffix)) {
 +                tag = tag.substring(0, tag.length() - suffix.length());
 +                break;
 +            }
 +        }
 +        return StringUtils.camelToSplitName(tag, "-");
 +    }
 +
 +    public static void appendParameters(Map<String, String> parameters, Object config) {
 +        appendParameters(parameters, config, null);
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    public static void appendParameters(Map<String, String> parameters, Object config, String prefix) {
 +        if (config == null) {
 +            return;
 +        }
 +        Method[] methods = config.getClass().getMethods();
 +        for (Method method : methods) {
 +            try {
 +                String name = method.getName();
 +                if (MethodUtils.isGetter(method)) {
 +                    Parameter parameter = method.getAnnotation(Parameter.class);
 +                    if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
 +                        continue;
 +                    }
 +                    String key;
 +                    if (parameter != null && parameter.key().length() > 0) {
 +                        key = parameter.key();
 +                    } else {
 +                        key = calculatePropertyFromGetter(name);
 +                    }
 +                    Object value = method.invoke(config);
 +                    String str = String.valueOf(value).trim();
 +                    if (value != null && str.length() > 0) {
 +                        if (parameter != null && parameter.escaped()) {
 +                            str = URL.encode(str);
 +                        }
 +                        if (parameter != null && parameter.append()) {
 +                            String pre = parameters.get(key);
 +                            if (pre != null && pre.length() > 0) {
 +                                str = pre + "," + str;
 +                            }
 +                        }
 +                        if (prefix != null && prefix.length() > 0) {
 +                            key = prefix + "." + key;
 +                        }
 +                        parameters.put(key, str);
 +                    } else if (parameter != null && parameter.required()) {
 +                        throw new IllegalStateException(config.getClass().getSimpleName() + "." + key + " == null");
 +                    }
 +                } else if (isParametersGetter(method)) {
 +                    Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);
 +                    parameters.putAll(convert(map, prefix));
 +                }
 +            } catch (Exception e) {
 +                throw new IllegalStateException(e.getMessage(), e);
 +            }
 +        }
 +    }
 +
 +    @Deprecated
 +    protected static void appendAttributes(Map<String, Object> parameters, Object config) {
 +        appendAttributes(parameters, config, null);
 +    }
 +
 +    @Deprecated
 +    protected static void appendAttributes(Map<String, Object> parameters, Object config, String prefix) {
 +        if (config == null) {
 +            return;
 +        }
 +        Method[] methods = config.getClass().getMethods();
 +        for (Method method : methods) {
 +            try {
 +                Parameter parameter = method.getAnnotation(Parameter.class);
 +                if (parameter == null || !parameter.attribute()) {
 +                    continue;
 +                }
 +                String name = method.getName();
 +                if (MethodUtils.isGetter(method)) {
 +                    String key;
 +                    if (parameter.key().length() > 0) {
 +                        key = parameter.key();
 +                    } else {
 +                        key = calculateAttributeFromGetter(name);
 +                    }
 +                    Object value = method.invoke(config);
 +                    if (value != null) {
 +                        if (prefix != null && prefix.length() > 0) {
 +                            key = prefix + "." + key;
 +                        }
 +                        parameters.put(key, value);
 +                    }
 +                }
 +            } catch (Exception e) {
 +                throw new IllegalStateException(e.getMessage(), e);
 +            }
 +        }
 +    }
 +
-     public static ConsumerModel.AsyncMethodInfo convertMethodConfig2AsyncInfo(MethodConfig methodConfig) {
++    protected static AsyncMethodInfo convertMethodConfig2AsyncInfo(MethodConfig methodConfig) {
 +        if (methodConfig == null || (methodConfig.getOninvoke() == null && methodConfig.getOnreturn() == null && methodConfig.getOnthrow() == null)) {
 +            return null;
 +        }
 +
 +        //check config conflict
 +        if (Boolean.FALSE.equals(methodConfig.isReturn()) && (methodConfig.getOnreturn() != null || methodConfig.getOnthrow() != null)) {
 +            throw new IllegalStateException("method config error : return attribute must be set true when onreturn or onthrow has been set.");
 +        }
 +
-         ConsumerModel.AsyncMethodInfo asyncMethodInfo = new ConsumerModel.AsyncMethodInfo();
++        AsyncMethodInfo asyncMethodInfo = new AsyncMethodInfo();
 +
 +        asyncMethodInfo.setOninvokeInstance(methodConfig.getOninvoke());
 +        asyncMethodInfo.setOnreturnInstance(methodConfig.getOnreturn());
 +        asyncMethodInfo.setOnthrowInstance(methodConfig.getOnthrow());
 +
 +        try {
 +            String oninvokeMethod = methodConfig.getOninvokeMethod();
 +            if (StringUtils.isNotEmpty(oninvokeMethod)) {
 +                asyncMethodInfo.setOninvokeMethod(getMethodByName(methodConfig.getOninvoke().getClass(), oninvokeMethod));
 +            }
 +
 +            String onreturnMethod = methodConfig.getOnreturnMethod();
 +            if (StringUtils.isNotEmpty(onreturnMethod)) {
 +                asyncMethodInfo.setOnreturnMethod(getMethodByName(methodConfig.getOnreturn().getClass(), onreturnMethod));
 +            }
 +
 +            String onthrowMethod = methodConfig.getOnthrowMethod();
 +            if (StringUtils.isNotEmpty(onthrowMethod)) {
 +                asyncMethodInfo.setOnthrowMethod(getMethodByName(methodConfig.getOnthrow().getClass(), onthrowMethod));
 +            }
 +        } catch (Exception e) {
 +            throw new IllegalStateException(e.getMessage(), e);
 +        }
 +
 +        return asyncMethodInfo;
 +    }
 +
 +    private static Method getMethodByName(Class<?> clazz, String methodName) {
 +        try {
 +            return ReflectUtils.findMethodByMethodName(clazz, methodName);
 +        } catch (Exception e) {
 +            throw new IllegalStateException(e);
 +        }
 +    }
 +
 +    protected static Set<String> getSubProperties(Map<String, String> properties, String prefix) {
 +        return properties.keySet().stream().filter(k -> k.contains(prefix)).map(k -> {
 +            k = k.substring(prefix.length());
 +            return k.substring(0, k.indexOf("."));
 +        }).collect(Collectors.toSet());
 +    }
 +
 +    private static String extractPropertyName(Class<?> clazz, Method setter) throws Exception {
 +        String propertyName = setter.getName().substring("set".length());
 +        Method getter = null;
 +        try {
 +            getter = clazz.getMethod("get" + propertyName);
 +        } catch (NoSuchMethodException e) {
 +            getter = clazz.getMethod("is" + propertyName);
 +        }
 +        Parameter parameter = getter.getAnnotation(Parameter.class);
 +        if (parameter != null && StringUtils.isNotEmpty(parameter.key()) && parameter.useKeyAsProperty()) {
 +            propertyName = parameter.key();
 +        } else {
 +            propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
 +        }
 +        return propertyName;
 +    }
 +
 +    private static String calculatePropertyFromGetter(String name) {
 +        int i = name.startsWith("get") ? 3 : 2;
 +        return StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), ".");
 +    }
 +
 +    private static String calculateAttributeFromGetter(String getter) {
 +        int i = getter.startsWith("get") ? 3 : 2;
 +        return getter.substring(i, i + 1).toLowerCase() + getter.substring(i + 1);
 +    }
 +
 +    private static void invokeSetParameters(Class c, Object o, Map map) {
 +        try {
 +            Method method = findMethodByMethodSignature(c, "setParameters", new String[]{Map.class.getName()});
 +            if (method != null && isParametersSetter(method)) {
 +                method.invoke(o, map);
 +            }
 +        } catch (Throwable t) {
 +            // ignore
 +        }
 +    }
 +
 +    private static Map<String, String> invokeGetParameters(Class c, Object o) {
 +        try {
 +            Method method = findMethodByMethodSignature(c, "getParameters", null);
 +            if (method != null && isParametersGetter(method)) {
 +                return (Map<String, String>) method.invoke(o);
 +            }
 +        } catch (Throwable t) {
 +            // ignore
 +        }
 +        return null;
 +    }
 +
 +    private static boolean isParametersGetter(Method method) {
 +        String name = method.getName();
 +        return ("getParameters".equals(name)
 +                && Modifier.isPublic(method.getModifiers())
 +                && method.getParameterTypes().length == 0
 +                && method.getReturnType() == Map.class);
 +    }
 +
 +    private static boolean isParametersSetter(Method method) {
 +        return ("setParameters".equals(method.getName())
 +                && Modifier.isPublic(method.getModifiers())
 +                && method.getParameterCount() == 1
 +                && Map.class == method.getParameterTypes()[0]
 +                && method.getReturnType() == void.class);
 +    }
 +
 +    private static Map<String, String> convert(Map<String, String> parameters, String prefix) {
 +        if (parameters == null || parameters.isEmpty()) {
 +            return Collections.emptyMap();
 +        }
 +
 +        Map<String, String> result = new HashMap<>();
 +        String pre = (prefix != null && prefix.length() > 0 ? prefix + "." : "");
 +        for (Map.Entry<String, String> entry : parameters.entrySet()) {
 +            String key = entry.getKey();
 +            String value = entry.getValue();
 +            result.put(pre + key, value);
 +            // For compatibility, key like "registry-type" will has a duplicate key "registry.type"
 +            if (key.contains("-")) {
 +                result.put(pre + key.replace('-', '.'), value);
 +            }
 +        }
 +        return result;
 +    }
 +
 +    @Parameter(excluded = true)
 +    public String getId() {
 +        return id;
 +    }
 +
 +    public void setId(String id) {
 +        this.id = id;
 +    }
 +
 +    public void updateIdIfAbsent(String value) {
 +        if (StringUtils.isNotEmpty(value) && StringUtils.isEmpty(id)) {
 +            this.id = value;
 +        }
 +    }
 +
 +    protected void appendAnnotation(Class<?> annotationClass, Object annotation) {
 +        Method[] methods = annotationClass.getMethods();
 +        for (Method method : methods) {
 +            if (method.getDeclaringClass() != Object.class
 +                    && method.getReturnType() != void.class
 +                    && method.getParameterTypes().length == 0
 +                    && Modifier.isPublic(method.getModifiers())
 +                    && !Modifier.isStatic(method.getModifiers())) {
 +                try {
 +                    String property = method.getName();
 +                    if ("interfaceClass".equals(property) || "interfaceName".equals(property)) {
 +                        property = "interface";
 +                    }
 +                    String setter = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);
 +                    Object value = method.invoke(annotation);
 +                    if (value != null && !value.equals(method.getDefaultValue())) {
 +                        Class<?> parameterType = ReflectUtils.getBoxedClass(method.getReturnType());
 +                        if ("filter".equals(property) || "listener".equals(property)) {
 +                            parameterType = String.class;
 +                            value = StringUtils.join((String[]) value, ",");
 +                        } else if ("parameters".equals(property)) {
 +                            parameterType = Map.class;
 +                            value = CollectionUtils.toStringMap((String[]) value);
 +                        }
 +                        try {
 +                            Method setterMethod = getClass().getMethod(setter, parameterType);
 +                            setterMethod.invoke(this, value);
 +                        } catch (NoSuchMethodException e) {
 +                            // ignore
 +                        }
 +                    }
 +                } catch (Throwable e) {
 +                    logger.error(e.getMessage(), e);
 +                }
 +            }
 +        }
 +    }
 +
 +    /**
 +     * Should be called after Config was fully initialized.
 +     * // FIXME: this method should be completely replaced by appendParameters
 +     *
 +     * @return
 +     * @see AbstractConfig#appendParameters(Map, Object, String)
 +     * <p>
 +     * Notice! This method should include all properties in the returning map, treat @Parameter differently compared to appendParameters.
 +     */
 +    public Map<String, String> getMetaData() {
 +        Map<String, String> metaData = new HashMap<>();
 +        Method[] methods = this.getClass().getMethods();
 +        for (Method method : methods) {
 +            try {
 +                String name = method.getName();
 +                if (MethodUtils.isMetaMethod(method)) {
 +                    String key;
 +                    Parameter parameter = method.getAnnotation(Parameter.class);
 +                    if (parameter != null && parameter.key().length() > 0 && parameter.useKeyAsProperty()) {
 +                        key = parameter.key();
 +                    } else {
 +                        key = calculateAttributeFromGetter(name);
 +                    }
 +                    // treat url and configuration differently, the value should always present in configuration though it may not need to present in url.
 +                    //if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
 +                    if (method.getReturnType() == Object.class) {
 +                        metaData.put(key, null);
 +                        continue;
 +                    }
 +
 +                    /**
 +                     * Attributes annotated as deprecated should not override newly added replacement.
 +                     */
 +                    if (MethodUtils.isDeprecated(method) && metaData.get(key) != null) {
 +                        continue;
 +                    }
 +
 +                    Object value = method.invoke(this);
 +                    String str = String.valueOf(value).trim();
 +                    if (value != null && str.length() > 0) {
 +                        metaData.put(key, str);
 +                    } else {
 +                        metaData.put(key, null);
 +                    }
 +                } else if (isParametersGetter(method)) {
 +                    Map<String, String> map = (Map<String, String>) method.invoke(this, new Object[0]);
 +                    metaData.putAll(convert(map, ""));
 +                }
 +            } catch (Exception e) {
 +                throw new IllegalStateException(e.getMessage(), e);
 +            }
 +        }
 +        return metaData;
 +    }
 +
 +    @Parameter(excluded = true)
 +    public String getPrefix() {
 +        return StringUtils.isNotEmpty(prefix) ? prefix : (CommonConstants.DUBBO + "." + getTagName(this.getClass()));
 +    }
 +
 +    public void setPrefix(String prefix) {
 +        this.prefix = prefix;
 +    }
 +
 +    public void refresh() {
 +        Environment env = ApplicationModel.getEnvironment();
 +        try {
 +            CompositeConfiguration compositeConfiguration = env.getConfiguration(getPrefix(), getId());
 +            Configuration config = new ConfigConfigurationAdapter(this);
 +            if (env.isConfigCenterFirst()) {
 +                // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
 +                compositeConfiguration.addConfiguration(4, config);
 +            } else {
 +                // The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
 +                compositeConfiguration.addConfiguration(2, config);
 +            }
 +
 +            // loop methods, get override value and set the new value back to method
 +            Method[] methods = getClass().getMethods();
 +            for (Method method : methods) {
 +                if (MethodUtils.isSetter(method)) {
 +                    try {
 +                        String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
 +                        // isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
 +                        if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
 +                            method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
 +                        }
 +                    } catch (NoSuchMethodException e) {
 +                        logger.info("Failed to override the property " + method.getName() + " in " +
 +                                this.getClass().getSimpleName() +
 +                                ", please make sure every property has getter/setter method provided.");
 +                    }
 +                } else if (isParametersSetter(method)) {
 +                    String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
 +                    if (StringUtils.isNotEmpty(value)) {
 +                        Map<String, String> map = invokeGetParameters(getClass(), this);
 +                        map = map == null ? new HashMap<>() : map;
 +                        map.putAll(convert(StringUtils.parseParameters(value), ""));
 +                        invokeSetParameters(getClass(), this, map);
 +                    }
 +                }
 +            }
 +        } catch (Exception e) {
 +            logger.error("Failed to override ", e);
 +        }
 +    }
 +
 +    @Override
 +    public String toString() {
 +        try {
 +            StringBuilder buf = new StringBuilder();
 +            buf.append("<dubbo:");
 +            buf.append(getTagName(getClass()));
 +            Method[] methods = getClass().getMethods();
 +            for (Method method : methods) {
 +                try {
 +                    if (MethodUtils.isGetter(method)) {
 +                        String name = method.getName();
 +                        String key = calculateAttributeFromGetter(name);
 +                        Object value = method.invoke(this);
 +                        if (value != null) {
 +                            buf.append(" ");
 +                            buf.append(key);
 +                            buf.append("=\"");
 +                            buf.append(value);
 +                            buf.append("\"");
 +                        }
 +                    }
 +                } catch (Exception e) {
 +                    logger.warn(e.getMessage(), e);
 +                }
 +            }
 +            buf.append(" />");
 +            return buf.toString();
 +        } catch (Throwable t) {
 +            logger.warn(t.getMessage(), t);
 +            return super.toString();
 +        }
 +    }
 +
 +    /**
 +     * FIXME check @Parameter(required=true) and any conditions that need to match.
 +     */
 +    @Parameter(excluded = true)
 +    public boolean isValid() {
 +        return true;
 +    }
 +
 +
 +    @Override
 +    public boolean equals(Object obj) {
 +        if (obj == null || !(obj.getClass().getName().equals(this.getClass().getName()))) {
 +            return false;
 +        }
 +
 +        Method[] methods = this.getClass().getMethods();
 +        for (Method method1 : methods) {
 +            if (MethodUtils.isGetter(method1)) {
 +                Parameter parameter = method1.getAnnotation(Parameter.class);
 +                if (parameter != null && parameter.excluded()) {
 +                    continue;
 +                }
 +                try {
 +                    Method method2 = obj.getClass().getMethod(method1.getName(), method1.getParameterTypes());
 +                    Object value1 = method1.invoke(this, new Object[]{});
 +                    Object value2 = method2.invoke(obj, new Object[]{});
 +                    if (!Objects.equals(value1, value2)) {
 +                        return false;
 +                    }
 +                } catch (Exception e) {
 +                    return true;
 +                }
 +            }
 +        }
 +        return true;
 +    }
 +
 +    /**
 +     * Add {@link AbstractConfig instance} into {@link ConfigManager}
 +     * <p>
 +     * Current method will invoked by Spring or Java EE container automatically, or should be triggered manually.
 +     *
 +     * @see ConfigManager#addConfig(AbstractConfig)
 +     * @since 2.7.5
 +     */
 +    @PostConstruct
 +    public void addIntoConfigManager() {
 +        ApplicationModel.getConfigManager().addConfig(this);
 +    }
 +
 +    @Override
 +    public int hashCode() {
 +        int hashCode = 1;
 +
 +        Method[] methods = this.getClass().getMethods();
 +        for (Method method : methods) {
 +            if (MethodUtils.isGetter(method)) {
 +                Parameter parameter = method.getAnnotation(Parameter.class);
 +                if (parameter != null && parameter.excluded()) {
 +                    continue;
 +                }
 +                try {
 +                    Object value = method.invoke(this, new Object[]{});
 +                    hashCode = 31 * hashCode + value.hashCode();
 +                } catch (Exception ignored) {
 +                    //ignored
 +                }
 +            }
 +        }
 +
 +        if (hashCode == 0) {
 +            hashCode = 1;
 +        }
 +
 +        return hashCode;
 +    }
 +}
diff --cc dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerMethodModel.java
index b164856,0000000..7a4427f
mode 100644,000000..100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerMethodModel.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ConsumerMethodModel.java
@@@ -1,147 -1,0 +1,101 @@@
 +/*
 + * 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.rpc.model;
 +
 +import java.lang.reflect.Method;
- import java.util.Map;
 +import java.util.concurrent.ConcurrentHashMap;
 +import java.util.concurrent.ConcurrentMap;
 +
 +import static org.apache.dubbo.common.constants.CommonConstants.$INVOKE;
 +
 +/**
 + * Replaced with {@link MethodDescriptor}
 + */
 +@Deprecated
 +public class ConsumerMethodModel {
 +    private final Method method;
 +    //    private final boolean isCallBack;
 +//    private final boolean isFuture;
 +    private final String[] parameterTypes;
 +    private final Class<?>[] parameterClasses;
 +    private final Class<?> returnClass;
 +    private final String methodName;
 +    private final boolean generic;
 +
-     private final AsyncMethodInfo asyncInfo;
 +    private final ConcurrentMap<String, Object> attributeMap = new ConcurrentHashMap<>();
 +
 +
-     public ConsumerMethodModel(Method method, Map<String, Object> attributes) {
++    public ConsumerMethodModel(Method method) {
 +        this.method = method;
 +        this.parameterClasses = method.getParameterTypes();
 +        this.returnClass = method.getReturnType();
 +        this.parameterTypes = this.createParamSignature(parameterClasses);
 +        this.methodName = method.getName();
 +        this.generic = methodName.equals($INVOKE) && parameterTypes != null && parameterTypes.length == 3;
- 
-         if (attributes != null) {
-             ConsumerModel.AsyncMethodInfo consumerAsyncInfo = (ConsumerModel.AsyncMethodInfo) attributes.get(methodName);
-             this.asyncInfo = new AsyncMethodInfo(consumerAsyncInfo);
-         } else {
-             asyncInfo = null;
-         }
- 
 +    }
 +
 +    public Method getMethod() {
 +        return method;
 +    }
 +
 +//    public ConcurrentMap<String, Object> getAttributeMap() {
 +//        return attributeMap;
 +//    }
 +
 +    public void addAttribute(String key, Object value) {
 +        this.attributeMap.put(key, value);
 +    }
 +
 +    public Object getAttribute(String key) {
 +        return this.attributeMap.get(key);
 +    }
 +
 +
 +    public Class<?> getReturnClass() {
 +        return returnClass;
 +    }
 +
-     public AsyncMethodInfo getAsyncInfo() {
-         return asyncInfo;
-     }
- 
 +    public String getMethodName() {
 +        return methodName;
 +    }
 +
 +    public String[] getParameterTypes() {
 +        return parameterTypes;
 +    }
 +
 +    private String[] createParamSignature(Class<?>[] args) {
 +        if (args == null || args.length == 0) {
 +            return new String[]{};
 +        }
 +        String[] paramSig = new String[args.length];
 +        for (int x = 0; x < args.length; x++) {
 +            paramSig[x] = args[x].getName();
 +        }
 +        return paramSig;
 +    }
 +
 +
 +    public boolean isGeneric() {
 +        return generic;
 +    }
 +
 +    public Class<?>[] getParameterClasses() {
 +        return parameterClasses;
 +    }
 +
 +
-     public static class AsyncMethodInfo {
-         private ConsumerModel.AsyncMethodInfo delegate;
- 
-         public AsyncMethodInfo(ConsumerModel.AsyncMethodInfo methodInfo) {
-             this.delegate = methodInfo;
-         }
- 
-         public Object getOninvokeInstance() {
-             return delegate.getOninvokeInstance();
-         }
- 
- 
-         public Method getOninvokeMethod() {
-             return delegate.getOninvokeMethod();
-         }
- 
-         public Object getOnreturnInstance() {
-             return delegate.getOnreturnInstance();
-         }
- 
-         public Method getOnreturnMethod() {
-             return delegate.getOnreturnMethod();
-         }
- 
-         public Object getOnthrowInstance() {
-             return delegate.getOnthrowInstance();
-         }
- 
-         public Method getOnthrowMethod() {
-             return delegate.getOnthrowMethod();
-         }
-     }
 +}
diff --cc dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Offline.java
index eae4aca,8e1c63b..4fd15e1
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Offline.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Offline.java
@@@ -59,18 -58,19 +59,18 @@@ public class Offline implements BaseCom
  
      public static boolean offline(String servicePattern) {
          boolean hasService = false;
 -        Collection<ProviderModel> providerModelList = ApplicationModel.allProviderModels();
 +
 +        Collection<ProviderModel> providerModelList = serviceRepository.getExportedServices();
          for (ProviderModel providerModel : providerModelList) {
-             if (providerModel.getServiceKey().matches(servicePattern)) {
+             if (providerModel.getServiceMetadata().getDisplayServiceKey().matches(servicePattern)) {
                  hasService = true;
 -                Set<ProviderInvokerWrapper> providerInvokerWrapperSet = ProviderConsumerRegTable
 -                        .getProviderInvoker(providerModel.getServiceMetadata().getServiceKey());
 -                for (ProviderInvokerWrapper providerInvokerWrapper : providerInvokerWrapperSet) {
 -                    if (!providerInvokerWrapper.isReg()) {
 -                        continue;
 +                List<ProviderModel.RegisterStatedURL> statedUrls = providerModel.getStatedUrl();
 +                for (ProviderModel.RegisterStatedURL statedURL : statedUrls) {
 +                    if (statedURL.isRegistered()) {
 +                        Registry registry = registryFactory.getRegistry(statedURL.getRegistryUrl());
 +                        registry.unregister(statedURL.getProviderUrl());
 +                        statedURL.setRegistered(false);
                      }
 -                    Registry registry = registryFactory.getRegistry(providerInvokerWrapper.getRegistryUrl());
 -                    registry.unregister(providerInvokerWrapper.getProviderUrl());
 -                    providerInvokerWrapper.setReg(false);
                  }
              }
          }
diff --cc dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Online.java
index 1f99dd5,cb62647..c04f184
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Online.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Online.java
@@@ -55,22 -56,23 +55,22 @@@ public class Online implements BaseComm
          } else {
              return "service not found";
          }
 -
      }
  
 -    public static boolean online(String servicePattern){
 +    public static boolean online(String servicePattern) {
          boolean hasService = false;
 -        Collection<ProviderModel> providerModelList = ApplicationModel.allProviderModels();
 +
 +        Collection<ProviderModel> providerModelList = serviceRepository.getExportedServices();
          for (ProviderModel providerModel : providerModelList) {
-             if (providerModel.getServiceName().matches(servicePattern)) {
+             if (providerModel.getServiceMetadata().getDisplayServiceKey().matches(servicePattern)) {
                  hasService = true;
 -                Set<ProviderInvokerWrapper> providerInvokerWrapperSet = ProviderConsumerRegTable.getProviderInvoker(providerModel.getServiceMetadata().getServiceKey());
 -                for (ProviderInvokerWrapper providerInvokerWrapper : providerInvokerWrapperSet) {
 -                    if (providerInvokerWrapper.isReg()) {
 -                        continue;
 +                List<ProviderModel.RegisterStatedURL> statedUrls = providerModel.getStatedUrl();
 +                for (ProviderModel.RegisterStatedURL statedURL : statedUrls) {
 +                    if (!statedURL.isRegistered()) {
 +                        Registry registry = registryFactory.getRegistry(statedURL.getRegistryUrl());
 +                        registry.register(statedURL.getProviderUrl());
 +                        statedURL.setRegistered(true);
                      }
 -                    Registry registry = registryFactory.getRegistry(providerInvokerWrapper.getRegistryUrl());
 -                    registry.register(providerInvokerWrapper.getProviderUrl());
 -                    providerInvokerWrapper.setReg(true);
                  }
              }
          }
diff --cc dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
index bcbe962,ffbdcec..1f182a0
--- a/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
+++ b/dubbo-rpc/dubbo-rpc-dubbo/src/main/java/org/apache/dubbo/rpc/protocol/dubbo/filter/FutureFilter.java
@@@ -49,22 -54,8 +50,22 @@@ public class FutureFilter implements Fi
          return invoker.invoke(invocation);
      }
  
 +    @Override
 +    public void onMessage(Result result, Invoker<?> invoker, Invocation invocation) {
 +        if (result.hasException()) {
 +            fireThrowCallback(invoker, invocation, result.getException());
 +        } else {
 +            fireReturnCallback(invoker, invocation, result.getValue());
 +        }
 +    }
 +
 +    @Override
 +    public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
 +
 +    }
 +
      private void fireInvokeCallback(final Invoker<?> invoker, final Invocation invocation) {
-         final ConsumerModel.AsyncMethodInfo asyncMethodInfo = getAsyncMethodInfo(invoker, invocation);
+         final AsyncMethodInfo asyncMethodInfo = getAsyncMethodInfo(invoker, invocation);
          if (asyncMethodInfo == null) {
              return;
          }
@@@ -195,12 -186,22 +196,7 @@@
              methodName = (String) invocation.getArguments()[0];
          }
  
-         final ConsumerModel.AsyncMethodInfo asyncMethodInfo = consumerModel.getMethodConfig(methodName);
-         if (asyncMethodInfo == null) {
-             return null;
-         }
- 
-         return asyncMethodInfo;
+         return consumerModel.getAsyncInfo(methodName);
      }
  
 -    class FutureListener implements Listener {
 -        @Override
 -        public void onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
 -            if (result.hasException()) {
 -                fireThrowCallback(invoker, invocation, result.getException());
 -            } else {
 -                fireReturnCallback(invoker, invocation, result.getValue());
 -            }
 -        }
 -
 -        @Override
 -        public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
 -
 -        }
 -    }
  }