美文网首页
Dubbo源码:AbstractConfig

Dubbo源码:AbstractConfig

作者: 无聊之园 | 来源:发表于2021-03-30 14:55 被阅读0次

    这个类定义了很多分析配置的工具方法。
    总体而言,比较简单,除了一些细节,没有一些值得研究的思想。

    1、设计。
    这个值得说一下的也就这个继承机制了:
    Config的继承的机制

    主要的的方法:
    appendParameters:分析对象获取参数。调用类的get开头方法,获取到值,以方法名为key,值为value,然后添加到传参map parameter。
    convertMethodConfig2AsyncInfo:转换对象的方法。
    extractPropertyName:解析属性名。通过get、set方法。
    appendAnnotation:获取对象的有返回值、无参、非静态、公共的方法,调用获取到值。然后调用这个”set" + 方法名的方法,把这个值set进去。
    refresh:刷新配置。也就是属性注入,从复合配置保存容器中获取这个配置需要的属性值(就是看set方法),然后设置注入进去。
    getMetaData:获取对象的有返回值、无参、非静态、公共的方法,调用获取到值。然后调用这个”set" + 方法名的方法,把这个值set进去。

    详细代码解读注释如下:

    
    /**
     * 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;
        // 这个map保存历史遗留属性,随着dubbo变更,有些属性名发生过变迁
        /**
         * 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;
        }
    // 获取标签名。SUFFIXES后缀有:"Config", "Bean", "ConfigBase"。
    // 再把驼峰命名,改成”-“分隔。
        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);
        }
    // 分析config参数的所有get方法,调用获取到值value,然后以get方法的方法
    // 名获取到属性名作为key,如果prefix前缀存在,则key,再前面再加上prefix 
    //+ "."前缀。之后存入传参parameters map。
    // 如果方法上存在Parameter注解,则依据注解的属性,判断是否分析这个方
    // 法;指定key;是否url编码value;是否不新增只是append叠加value;
    // 如果方法上被getParameters存在这个注解,则说明不是单一parameter,而
    //是一个集合map,直接调用,然后获取到的map,全部添加到传参parameters
    //中。
        @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);
                }
            }
        }
    // 这个方法和appendParameters很类似,只不过没有appendParameters方法
    //的很多额外的逻辑。这个方法只是分析get方法,然后添加到parameters中。
    //也没有驼峰命名转成”-“分隔。
    //这个方法也被Deprecated注释了,所以可以忽略。 
        @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);
                }
            }
        }
    // 转换对象,从MethodConfig对象转成AsyncMethodInfo对象,一系列get、set操作。暂且不看这两个对象是干什么用的,为什么要转换。
        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.");
            }
    
            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;
        }
    // 反射获取Method对象。ReflectUtils工具类,基本上每一个框架都有这个玩意。
        private static Method getMethodByName(Class<?> clazz, String methodName) {
            try {
                return ReflectUtils.findMethodByMethodName(clazz, methodName);
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
        }
    // 获取子属性。比如a.b.c,a.b.d,然后传参a.b, 获取到c、d。
        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;
            }
        }
    // 获取对象的有返回值、无参、非静态、公共的方法,调用获取到值。
    // 然后调用这个”set" + 方法名的方法,把这个值set进去。
        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.
         */
    // 获取metaData,就是把get、is方法,调用获取到值,保存到一个对象然后返回。
        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;
        }
    // 刷新配置。也就是,从符合配置保存类中获取这个配置需要的属性值(就是看set方法),然后设置进去。
        public void refresh() {
            Environment env = ApplicationModel.getEnvironment();
            try {
                CompositeConfiguration compositeConfiguration = env.getPrefixedConfiguration(this);
                // 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);
    
                            try {
                                getClass().getDeclaredField(key);
                            } catch (NoSuchFieldException e) {
                                // ignore
                                continue;
                            }
    
                            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;
        }
    
    // 判断相等,就是根据各个get方法的值是否相等。
        @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;
        }
    }
    

    相关文章

      网友评论

          本文标题:Dubbo源码:AbstractConfig

          本文链接:https://www.haomeiwen.com/subject/yltghltx.html