zoukankan      html  css  js  c++  java
  • 占位符解析

    占位符解析过程

    占位符解析器
    /**
     * 从指定的属性源中,将占位符解析为具体的值
     */
    public class PropertyPlaceholderHelper {
    
        private static final Log logger = LogFactory.getLog(PropertyPlaceholderHelper.class);
    
        private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<>(4);
    
        static {
            wellKnownSimplePrefixes.put("}", "{");
            wellKnownSimplePrefixes.put("]", "[");
            wellKnownSimplePrefixes.put(")", "(");
        }
        /**
         * 占位符前缀
         */
        private final String placeholderPrefix;
        /**
         * 占位符后缀
         */
        private final String placeholderSuffix;
    
        private final String simplePrefix;
        /**
         * 默认值分隔符
         */
        @Nullable
        private final String valueSeparator;
        /**
         * 是否忽略无法解析的占位符
         */
        private final boolean ignoreUnresolvablePlaceholders;
    
        public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
            this(placeholderPrefix, placeholderSuffix, null, true);
        }
    
        public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
                @Nullable String valueSeparator, boolean ignoreUnresolvablePlaceholders) {
            Assert.notNull(placeholderPrefix, "'placeholderPrefix' must not be null");
            Assert.notNull(placeholderSuffix, "'placeholderSuffix' must not be null");
            this.placeholderPrefix = placeholderPrefix;
            this.placeholderSuffix = placeholderSuffix;
            final String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
            if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
                simplePrefix = simplePrefixForSuffix;
            }
            else {
                simplePrefix = this.placeholderPrefix;
            }
            this.valueSeparator = valueSeparator;
            this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
        }
    
        /**
         * 将占位符替换为具体的值
         */
        public String replacePlaceholders(String value, final Properties properties) {
            Assert.notNull(properties, "'properties' must not be null");
            return replacePlaceholders(value, properties::getProperty);
        }
    
        public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
            Assert.notNull(value, "'value' must not be null");
            return parseStringValue(value, placeholderResolver, new HashSet<>());
        }
    
        protected String parseStringValue(
                String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
            final StringBuilder result = new StringBuilder(value);
            // 读取占位符前缀在目标字符串中的索引
            int startIndex = value.indexOf(placeholderPrefix);
            while (startIndex != -1) {
                // 读取占位符后缀在目标字符串中的索引
                final int endIndex = findPlaceholderEndIndex(result, startIndex);
                if (endIndex != -1) {
                    // 提取占位符值
                    String placeholder = result.substring(startIndex + placeholderPrefix.length(), endIndex);
                    final String originalPlaceholder = placeholder;
                    if (!visitedPlaceholders.add(originalPlaceholder)) {
                        throw new IllegalArgumentException(
                                "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
                    }
                    // 递归解析占位符中包含的占位符
                    placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
                    // 将占位符解析为具体的值
                    String propVal = placeholderResolver.resolvePlaceholder(placeholder);
                    if (propVal == null && valueSeparator != null) {
                        /**
                         *  如果未找到,则尝试进行 ${name:zxd} 格式的解析
                         *  读取值分隔符索引
                         */
                        final int separatorIndex = placeholder.indexOf(valueSeparator);
                        if (separatorIndex != -1) {
                            // 截取实际的占位符
                            final String actualPlaceholder = placeholder.substring(0, separatorIndex);
                            // 截取默认值
                            final String defaultValue = placeholder.substring(separatorIndex + valueSeparator.length());
                            // 将占位符解析为具体的值
                            propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                            if (propVal == null) {
                                // 如果不存在,则使用默认值
                                propVal = defaultValue;
                            }
                        }
                    }
                    // 值解析成功
                    if (propVal != null) {
                        // Recursive invocation, parsing placeholders contained in the previously resolved placeholder value.
                        propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                        result.replace(startIndex, endIndex + placeholderSuffix.length(), propVal);
                        if (logger.isTraceEnabled()) {
                            logger.trace("Resolved placeholder '" + placeholder + "'");
                        }
                        startIndex = result.indexOf(placeholderPrefix, startIndex + propVal.length());
                    }
                    else if (ignoreUnresolvablePlaceholders) {
                        // Proceed with unprocessed value.
                        startIndex = result.indexOf(placeholderPrefix, endIndex + placeholderSuffix.length());
                    }
                    else {
                        throw new IllegalArgumentException("Could not resolve placeholder '" +
                                placeholder + "'" + " in value "" + value + """);
                    }
                    // 移除解析过的占位符
                    visitedPlaceholders.remove(originalPlaceholder);
                }
                else {
                    startIndex = -1;
                }
            }
    
            // 返回结果值
            return result.toString();
        }
    
        private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
            int index = startIndex + placeholderPrefix.length();
            int withinNestedPlaceholder = 0;
            while (index < buf.length()) {
                if (StringUtils.substringMatch(buf, index, placeholderSuffix)) {
                    if (withinNestedPlaceholder > 0) {
                        withinNestedPlaceholder--;
                        index = index + placeholderSuffix.length();
                    }
                    else {
                        return index;
                    }
                }
                else if (StringUtils.substringMatch(buf, index, simplePrefix)) {
                    withinNestedPlaceholder++;
                    index = index + simplePrefix.length();
                }
                else {
                    index++;
                }
            }
            return -1;
        }
    
        /**
         * 策略接口:用于将占位符替换为具体的值
         */
        @FunctionalInterface
        public interface PlaceholderResolver {
            /**
             * 将占位符替换为具体的值
             */
            @Nullable
            String resolvePlaceholder(String placeholderName);
        }
    }
    

    属性源

    • org.springframework.core.env.PropertySource:命名属性源
    public abstract class PropertySource<T> {
        protected final Log logger = LogFactory.getLog(getClass());
        /**
         * 属性源名称
         */
        protected final String name;
        /**
         * 具体的属性源:
         * java.util.Properties
         * java.util.Map
         * javax.servlet.ServletContext
         * javax.servlet.ServletConfig
         */
        protected final T source;
    
        /**
         * Return the name of this {@code PropertySource}.
         */
        public String getName() {
            return this.name;
        }
    
        /**
         * Return the underlying source object for this {@code PropertySource}.
         */
        public T getSource() {
            return this.source;
        }
    
        /**
         * Return whether this {@code PropertySource} contains the given name.
         */
        public boolean containsProperty(String name) {
            return getProperty(name) != null;
        }
    
        /**
         * Return the value associated with the given name, or {@code null} if not found.
         */
        @Nullable
        public abstract Object getProperty(String name);
    }
    
    • org.springframework.context.annotation.PropertySource:方便加载资源的注解
    /**
     *  以声明的方式将指定的属性文件解析为 PropertySource 并加入到 Environment 中,
     *  多个属性文件中存在相同的键时,后加入的键值对将覆盖先加入的。
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Repeatable(PropertySources.class)
    public @interface PropertySource {
    
        /**
         * 属性源的名称,如果未指定,则使用 org.springframework.core.io.Resource#getDescription() 生成。
         */
        String name() default "";
    
        /**
         * 指定资源的路径,例如 classpath:/config/app.properties 或 file:/path/to/file.xml
         * 资源路径不支持通配符,一个路径只能精确加载一个资源文件。
         * ${...} 占位符将基于已经注册的属性进行解析,解析成功后再加载资源。
         */
        String[] value();
    
        /**
         * 是否忽略不出在的资源文件
         */
        boolean ignoreResourceNotFound() default false;
    
        /**
         * 资源文件的编码 "UTF-8"
         */
        String encoding() default "";
    
        /**
         * 生成 PropertySource 的工厂类和具体的实例类型
         * @see org.springframework.core.io.support.DefaultPropertySourceFactory
         * @see org.springframework.core.io.support.ResourcePropertySource
         */
        Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
    }
    
    • org.springframework.core.env.PropertySources:聚合一个或多个属性源
    /**
     *  封装了一个或多个 PropertySource 实例
     */
    public interface PropertySources extends Iterable<PropertySource<?>> {
        /**
         * Return a sequential {@link Stream} containing the property sources.
         * @since 5.1
         */
        default Stream<PropertySource<?>> stream() {
            return StreamSupport.stream(spliterator(), false);
        }
    
        /**
         * 是否包含指定名称的属性源
         */
        boolean contains(String name);
    
        /**
         * 根据名称读取属性源
         */
        @Nullable
        PropertySource<?> get(String name);
    }
    
    • org.springframework.context.annotation.PropertySources:声明式添加一个或多个属性源
    /**
     * Container annotation that aggregates several {@link PropertySource} annotations.
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface PropertySources {
        PropertySource[] value();
    }
    

    属性解析器

    • org.springframework.core.env.PropertyResolver:属性解析器
    /**
     * 基于底层的数据源解析目标属性
     */
    public interface PropertyResolver {
        /**
         * 底层数据源是否包含目标属性
         */
        boolean containsProperty(String key);
        /**
         * 根据指定的 key 读取属性值,如果不存在,则返回 null
         */
        @Nullable
        String getProperty(String key);
        /**
         * 根据指定的 key 读取属性值,如果不存在,则返回 defaultValue
         */
        String getProperty(String key, String defaultValue);
    
        /**
         * 根据指定的 key 读取属性值,并将其转换为 T 类型
         */
        @Nullable
        <T> T getProperty(String key, Class<T> targetType);
    
        /**
         * 根据指定的 key 读取属性值,并将其转换为 T 类型,如果不存在,则返回 defaultValue
         */
        <T> T getProperty(String key, Class<T> targetType, T defaultValue);
    
        /**
         * 根据指定的 key 读取属性值,如果值不存在,则抛出 IllegalStateException 异常
         */
        String getRequiredProperty(String key) throws IllegalStateException;
    
        /**
         * 根据指定的 key 读取属性值,并将其转换为 T 类型,如果值不存在,则抛出 IllegalStateException 异常
         */
        <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
    
        /**
         * 将占位符解析为具体的属性值,
         * 无法解析的占位符 && 无默认值,则原样返回
         */
        String resolvePlaceholders(String text);
    
        /**
         * 将占位符解析为具体的属性值,
         * 无法解析的占位符 && 无默认值将抛出 IllegalArgumentException 异常
         */
        String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
    }
    
    • org.springframework.core.env.PropertySourcesPropertyResolver:属性源属性解析器
    public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
        /**
         * 1)environmentProperties:StandardServletEnvironment
         *  属性来源及优先级
         *  configurationProperties
         *  commandLineArgs
         *  servletConfigInitParams
         *  servletContextInitParams
         *  systemProperties
         *  systemEnvironment
         *  random
         * 2)localProperties:自定义属性文件
         */
        @Nullable
        private final PropertySources propertySources;
    
        public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) {
            this.propertySources = propertySources;
        }
    
        /**
         * 是否包含指定的属性
         */
        @Override
        public boolean containsProperty(String key) {
            if (propertySources != null) {
                for (final PropertySource<?> propertySource : propertySources) {
                    if (propertySource.containsProperty(key)) {
                        return true;
                    }
                }
            }
            return false;
        }
    
        /**
         * 读取属性值
         */
        @Override
        @Nullable
        public String getProperty(String key) {
            return getProperty(key, String.class, true);
        }
    
        /**
         * 读取属性值
         */
        @Override
        @Nullable
        public <T> T getProperty(String key, Class<T> targetValueType) {
            return getProperty(key, targetValueType, true);
        }
    
        /**
         * 读取属性值
         */
        @Override
        @Nullable
        protected String getPropertyAsRawString(String key) {
            return getProperty(key, String.class, false);
        }
    
        @Nullable
        protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
            if (propertySources != null) {
                for (final PropertySource<?> propertySource : propertySources) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Searching for key '" + key + "' in PropertySource '" +
                                propertySource.getName() + "'");
                    }
                    // 从当前属性源中读取属性
                    Object value = propertySource.getProperty(key);
                    if (value != null) {
                        // 如果存在 && 需要解析内嵌的占位符
                        if (resolveNestedPlaceholders && value instanceof String) {
                            value = resolveNestedPlaceholders((String) value);
                        }
                        logKeyFound(key, propertySource, value);
                        // 将读取的属性转换为合适的类型
                        return convertValueIfNecessary(value, targetValueType);
                    }
                }
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Could not find key '" + key + "' in any property source");
            }
            return null;
        }
    
        /**
         * Log the given key as found in the given {@link PropertySource}, resulting in
         * the given value.
         */
        protected void logKeyFound(String key, PropertySource<?> propertySource, Object value) {
            if (logger.isDebugEnabled()) {
                logger.debug("Found key '" + key + "' in PropertySource '" + propertySource.getName() +
                        "' with value of type " + value.getClass().getSimpleName());
            }
        }
    }
    

    实例

    @RestController
    @RequestMapping("/resolver")
    public class PlaceholdersController {
        @Autowired
        private StandardEnvironment environment;
    
        @GetMapping("/{placeHolder}")
        public String resolve(@PathVariable("placeHolder") String placeHolder) {
            return environment.resolvePlaceholders(placeHolder);
        }
    }
    
    http://localhost:8080/resolver/${local.server.port} 返回 8080
    
  • 相关阅读:
    android 本地字符串存取
    2020-07-17:线上一个服务有4个实例突然变得访问很慢,你会从什么地方入手找原因?
    2020-07-16:如何获得一个链表的倒数第n个元素?
    2020-07-15:死锁与活锁的区别,死锁与饥饿的区别?
    2020-07-14:es用过冷热分离吗?假如现在有些数据热变冷,有些数据冷变热,怎么解决?
    2020-07-28:已知sqrt (2)约等于 1.414,要求不用数学库,求sqrt (2)精确到小数点后 10 位。
    2020-07-29:从 innodb 的索引结构分析,为什么索引的 key 长度不能太长?
    2020-07-27:如何设计一个分布式文件系统,如何设计动态扩容和数据定位?
    2020-07-26:如何用 socket 编程实现 ftp 协议?
    2020-07-25:如何实现一个高效的单向链表逆序输出?
  • 原文地址:https://www.cnblogs.com/zhuxudong/p/10274401.html
Copyright © 2011-2022 走看看