Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
|
ce41f4d407 | |
|
bede11a7e2 | |
|
9388aa7f65 | |
|
4b106fb992 | |
|
9b61ef68d2 | |
|
0a75c0b8c0 | |
|
eb8b10f3f3 | |
|
b209b291c2 | |
|
fcf008405b | |
|
1ada9f4e88 |
|
@ -30,9 +30,7 @@
|
|||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-spring-context</artifactId>
|
||||
<version>${revision}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -17,19 +17,15 @@
|
|||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- java-->
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>${javax.annotation-api.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Nacos -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>${nacos.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.yaml</groupId>
|
||||
|
@ -41,38 +37,30 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.13.4</version>
|
||||
</dependency>
|
||||
|
||||
<!--yaml-->
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snake.yaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Framework -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Context Extras -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.spring</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
|
@ -80,30 +68,27 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.2.9</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -406,9 +406,11 @@ public abstract class AbstractAnnotationBeanPostProcessor implements Instantiati
|
|||
|
||||
if (injectedObject == null) {
|
||||
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
|
||||
if (injectedObject != null) {
|
||||
// Customized inject-object if necessary
|
||||
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
|
||||
}
|
||||
}
|
||||
|
||||
return injectedObject;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.alibaba.nacos.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -181,7 +182,7 @@ public class NacosValueAnnotationBeanPostProcessor
|
|||
}
|
||||
|
||||
private Object resolveNotifyValue(String nacosValueExpr, String key, String newValue) {
|
||||
String spelExpr = nacosValueExpr.replaceAll("\\$\\{" + key + PLACEHOLDER_SUFFIX, newValue);
|
||||
String spelExpr = StringUtils.replace(nacosValueExpr, PLACEHOLDER_PREFIX + key + PLACEHOLDER_SUFFIX, newValue);
|
||||
return resolveStringValue(spelExpr);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* 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 com.alibaba.nacos.spring.context.annotation.config;
|
||||
|
||||
import com.alibaba.nacos.common.utils.MD5Utils;
|
||||
import com.alibaba.nacos.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor;
|
||||
import com.alibaba.nacos.spring.context.event.config.NacosConfigReceivedEvent;
|
||||
import com.alibaba.nacos.spring.util.NacosUtils;
|
||||
import com.alibaba.nacos.spring.util.PlaceholderHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.annotation.InjectionMetadata;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
import static org.springframework.core.annotation.AnnotationUtils.getAnnotation;
|
||||
|
||||
/**
|
||||
* @author wuhaoqiang
|
||||
* @since 1.1.2
|
||||
**/
|
||||
public class SpringValueAnnotationBeanPostProcessor
|
||||
extends AbstractAnnotationBeanPostProcessor implements BeanFactoryAware,
|
||||
EnvironmentAware, ApplicationListener<NacosConfigReceivedEvent> {
|
||||
|
||||
/**
|
||||
* The name of {@link SpringValueAnnotationBeanPostProcessor} bean.
|
||||
*/
|
||||
public static final String BEAN_NAME = "StringValueAnnotationBeanPostProcessor";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/**
|
||||
* placeholder, valueTarget.
|
||||
*/
|
||||
private Map<String, List<StringValueTarget>> placeholderStringValueTargetMap = new HashMap<String, List<StringValueTarget>>();
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private Environment environment;
|
||||
|
||||
private BeanExpressionResolver exprResolver;
|
||||
|
||||
private BeanExpressionContext exprContext;
|
||||
|
||||
public SpringValueAnnotationBeanPostProcessor() {
|
||||
super(Value.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
|
||||
throw new IllegalArgumentException(
|
||||
"StringValueAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory");
|
||||
}
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
this.exprResolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver();
|
||||
this.exprContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean,
|
||||
String beanName, Class<?> injectedType,
|
||||
InjectionMetadata.InjectedElement injectedElement) throws Exception {
|
||||
Object value = resolveStringValue(attributes.getString("value"));
|
||||
Member member = injectedElement.getMember();
|
||||
if (member instanceof Field) {
|
||||
return convertIfNecessary((Field) member, value);
|
||||
}
|
||||
|
||||
if (member instanceof Method) {
|
||||
return convertIfNecessary((Method) member, value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildInjectedObjectCacheKey(AnnotationAttributes attributes,
|
||||
Object bean, String beanName, Class<?> injectedType,
|
||||
InjectionMetadata.InjectedElement injectedElement) {
|
||||
return bean.getClass().getName() + attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, final String beanName)
|
||||
throws BeansException {
|
||||
|
||||
doWithFields(bean, beanName);
|
||||
|
||||
doWithMethods(bean, beanName);
|
||||
|
||||
return super.postProcessBeforeInitialization(bean, beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(NacosConfigReceivedEvent event) {
|
||||
if (StringUtils.isEmpty(event)) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> updateProperties = NacosUtils.toProperties(event.getContent(), event.getType());
|
||||
|
||||
for (Map.Entry<String, List<StringValueTarget>> entry : placeholderStringValueTargetMap
|
||||
.entrySet()) {
|
||||
|
||||
String key = environment.resolvePlaceholders(entry.getKey());
|
||||
// Process modified keys, excluding deleted keys
|
||||
if (!updateProperties.containsKey(key)) {
|
||||
continue;
|
||||
}
|
||||
String newValue = environment.getProperty(key);
|
||||
|
||||
if (newValue == null) {
|
||||
continue;
|
||||
}
|
||||
List<StringValueTarget> beanPropertyList = entry.getValue();
|
||||
for (StringValueTarget target : beanPropertyList) {
|
||||
String md5String = MD5Utils.md5Hex(newValue, "UTF-8");
|
||||
boolean isUpdate = !target.lastMD5.equals(md5String);
|
||||
if (isUpdate) {
|
||||
target.updateLastMD5(md5String);
|
||||
Object evaluatedValue = resolveStringValue(target.stringValueExpr);
|
||||
if (target.method == null) {
|
||||
setField(target, evaluatedValue);
|
||||
} else {
|
||||
setMethod(target, evaluatedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object resolveStringValue(String strVal) {
|
||||
String value = beanFactory.resolveEmbeddedValue(strVal);
|
||||
if (exprResolver != null && value != null) {
|
||||
return exprResolver.evaluate(value, exprContext);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private Object convertIfNecessary(Field field, Object value) {
|
||||
TypeConverter converter = beanFactory.getTypeConverter();
|
||||
return converter.convertIfNecessary(value, field.getType(), field);
|
||||
}
|
||||
|
||||
private Object convertIfNecessary(Method method, Object value) {
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
Object[] arguments = new Object[paramTypes.length];
|
||||
|
||||
TypeConverter converter = beanFactory.getTypeConverter();
|
||||
|
||||
if (arguments.length == 1) {
|
||||
return converter.convertIfNecessary(value, paramTypes[0],
|
||||
new MethodParameter(method, 0));
|
||||
}
|
||||
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
arguments[i] = converter.convertIfNecessary(value, paramTypes[i],
|
||||
new MethodParameter(method, i));
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private void doWithFields(final Object bean, final String beanName) {
|
||||
ReflectionUtils.doWithFields(bean.getClass(),
|
||||
new ReflectionUtils.FieldCallback() {
|
||||
@Override
|
||||
public void doWith(Field field) throws IllegalArgumentException {
|
||||
Value annotation = getAnnotation(field, Value.class);
|
||||
doWithAnnotation(beanName, bean, annotation, field.getModifiers(),
|
||||
null, field);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void doWithMethods(final Object bean, final String beanName) {
|
||||
ReflectionUtils.doWithMethods(bean.getClass(),
|
||||
new ReflectionUtils.MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException {
|
||||
Value annotation = getAnnotation(method, Value.class);
|
||||
doWithAnnotation(beanName, bean, annotation,
|
||||
method.getModifiers(), method, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void doWithAnnotation(String beanName, Object bean, Value annotation,
|
||||
int modifiers, Method method, Field field) {
|
||||
if (annotation != null) {
|
||||
if (Modifier.isStatic(modifiers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> placeholderList = PlaceholderHelper.findPlaceholderKeys(annotation.value());
|
||||
for (String placeholder : placeholderList) {
|
||||
StringValueTarget stringValueTarget = new StringValueTarget(bean, beanName,
|
||||
method, field, annotation.value());
|
||||
put2ListMap(placeholderStringValueTargetMap, placeholder,
|
||||
stringValueTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <K, V> void put2ListMap(Map<K, List<V>> map, K key, V value) {
|
||||
List<V> valueList = map.get(key);
|
||||
if (valueList == null) {
|
||||
valueList = new ArrayList<V>();
|
||||
}
|
||||
valueList.add(value);
|
||||
map.put(key, valueList);
|
||||
}
|
||||
|
||||
private void setMethod(StringValueTarget stringValueTarget, Object propertyValue) {
|
||||
Method method = stringValueTarget.method;
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
try {
|
||||
method.invoke(stringValueTarget.bean,
|
||||
convertIfNecessary(method, propertyValue));
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Update value with {} (method) in {} (bean) with {}",
|
||||
method.getName(), stringValueTarget.beanName, propertyValue);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Can't update value with " + method.getName()
|
||||
+ " (method) in " + stringValueTarget.beanName + " (bean)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setField(final StringValueTarget stringValueTarget,
|
||||
final Object propertyValue) {
|
||||
final Object bean = stringValueTarget.bean;
|
||||
|
||||
Field field = stringValueTarget.field;
|
||||
|
||||
String fieldName = field.getName();
|
||||
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
field.set(bean, convertIfNecessary(field, propertyValue));
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Update value of the {}" + " (field) in {} (bean) with {}",
|
||||
fieldName, stringValueTarget.beanName, propertyValue);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Can't update value of the " + fieldName + " (field) in "
|
||||
+ stringValueTarget.beanName + " (bean)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class StringValueTarget {
|
||||
|
||||
private final Object bean;
|
||||
|
||||
private final String beanName;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Field field;
|
||||
|
||||
private String lastMD5;
|
||||
|
||||
private final String stringValueExpr;
|
||||
|
||||
StringValueTarget(Object bean, String beanName, Method method, Field field, String stringValueExpr) {
|
||||
this.bean = bean;
|
||||
|
||||
this.beanName = beanName;
|
||||
|
||||
this.method = method;
|
||||
|
||||
this.field = field;
|
||||
|
||||
this.lastMD5 = "";
|
||||
|
||||
this.stringValueExpr = stringValueExpr;
|
||||
}
|
||||
|
||||
protected void updateLastMD5(String newMD5) {
|
||||
this.lastMD5 = newMD5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -174,7 +174,7 @@ public class EventPublishingConfigService
|
|||
|
||||
/**
|
||||
* Destroy lifecycle method to invoke {@link #shutDown()}
|
||||
* @throws Exception
|
||||
* @throws Exception throw exception
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Override
|
||||
|
|
|
@ -87,7 +87,7 @@ public enum FileTypeEnum {
|
|||
* Get the corresponding FileTypeEnum by file extension or fileType. If not found FileTypeEnum.TEXT is returned
|
||||
*
|
||||
* @param extOrFileType file extension or fileType
|
||||
* @return
|
||||
* @return return {@link FileTypeEnum}
|
||||
*/
|
||||
public static FileTypeEnum getFileTypeEnumByFileExtensionOrFileType(String extOrFileType) {
|
||||
if (StringUtils.isNotBlank(extOrFileType)) {
|
||||
|
|
|
@ -92,7 +92,8 @@ public final class ConfigParseUtils {
|
|||
|
||||
if (DEFAULT_CONFIG_PARSE_MAP.containsKey(type)) {
|
||||
ConfigParse configParse = DEFAULT_CONFIG_PARSE_MAP.get(type);
|
||||
return configParse.parse(context);
|
||||
Map<String, Object> parseMap = configParse.parse(context);
|
||||
return parseMap == null ? new HashMap<>() : parseMap;
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException(
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.alibaba.nacos.spring.context.annotation.config.SpringValueAnnotationBeanPostProcessor;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
|
@ -362,6 +363,13 @@ public abstract class NacosBeanUtils {
|
|||
NacosValueAnnotationBeanPostProcessor.class);
|
||||
}
|
||||
|
||||
public static void registerStringValueAnnotationBeanPostProcessor(
|
||||
BeanDefinitionRegistry registry) {
|
||||
registerInfrastructureBeanIfAbsent(registry,
|
||||
SpringValueAnnotationBeanPostProcessor.BEAN_NAME,
|
||||
SpringValueAnnotationBeanPostProcessor.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register Nacos Common Beans
|
||||
*
|
||||
|
@ -398,6 +406,8 @@ public abstract class NacosBeanUtils {
|
|||
|
||||
registerNacosValueAnnotationBeanPostProcessor(registry);
|
||||
|
||||
registerStringValueAnnotationBeanPostProcessor(registry);
|
||||
|
||||
registerConfigServiceBeanBuilder(registry);
|
||||
|
||||
registerLoggingNacosConfigMetadataEventListener(registry);
|
||||
|
|
|
@ -532,6 +532,7 @@ public abstract class NacosUtils {
|
|||
* @param group config group
|
||||
* @param text config context
|
||||
* @param type config type
|
||||
* @return map format of result
|
||||
*/
|
||||
public static Map<String, Object> toProperties(String dataId, String group,
|
||||
String text, String type) {
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package com.alibaba.nacos.spring.util;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* @author wuhaoqiang
|
||||
* @since 1.1.2
|
||||
**/
|
||||
public class PlaceholderHelper {
|
||||
|
||||
private static final String PLACEHOLDER_PREFIX = "${";
|
||||
private static final String PLACEHOLDER_SUFFIX = "}";
|
||||
private static final String VALUE_SEPARATOR = ":";
|
||||
private static final String SIMPLE_PLACEHOLDER_PREFIX = "{";
|
||||
|
||||
/**
|
||||
* find keys from placeholder
|
||||
* ${key} - "key"
|
||||
* xxx${key}yyy - "key"
|
||||
* ${key:${key2:1}} - "key", "key2"
|
||||
* ${${key}} - "key"
|
||||
* ${${key:100}} - "key"
|
||||
* ${${key}:${key2}} - "key", "key2"
|
||||
* @param propertyString ${key}
|
||||
* @return key
|
||||
*/
|
||||
public static Set<String> findPlaceholderKeys(String propertyString) {
|
||||
Set<String> placeholderKeys = Sets.newHashSet();
|
||||
|
||||
if (Strings.isNullOrEmpty(propertyString) ||
|
||||
!(propertyString.contains(PLACEHOLDER_PREFIX) && propertyString.contains(PLACEHOLDER_SUFFIX))) {
|
||||
return placeholderKeys;
|
||||
}
|
||||
// handle xxx${yyy}zzz -> ${yyy}zzz
|
||||
propertyString = propertyString.substring(propertyString.indexOf(PLACEHOLDER_PREFIX));
|
||||
|
||||
Stack<String> stack = new Stack<String>();
|
||||
stack.push(propertyString);
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
String strVal = stack.pop();
|
||||
int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);
|
||||
if (startIndex == -1) {
|
||||
placeholderKeys.add(strVal);
|
||||
continue;
|
||||
}
|
||||
int endIndex = findPlaceholderEndIndex(strVal, startIndex);
|
||||
if (endIndex == -1) {
|
||||
// invalid placeholder
|
||||
continue;
|
||||
}
|
||||
|
||||
String placeholderCandidate = strVal.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
|
||||
|
||||
// ${key}
|
||||
// startsWith '${' continue
|
||||
if (placeholderCandidate.startsWith(PLACEHOLDER_PREFIX)) {
|
||||
stack.push(placeholderCandidate);
|
||||
} else {
|
||||
// exist ':' -> key:${key2:2}
|
||||
int separatorIndex = placeholderCandidate.indexOf(VALUE_SEPARATOR);
|
||||
|
||||
if (separatorIndex == -1) {
|
||||
stack.push(placeholderCandidate);
|
||||
} else {
|
||||
stack.push(placeholderCandidate.substring(0, separatorIndex));
|
||||
String defaultValuePart =
|
||||
normalizeToPlaceholder(placeholderCandidate.substring(separatorIndex + VALUE_SEPARATOR.length()));
|
||||
if (!Strings.isNullOrEmpty(defaultValuePart)) {
|
||||
stack.push(defaultValuePart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// has remaining part, e.g. ${a}.${b}
|
||||
if (endIndex + PLACEHOLDER_SUFFIX.length() < strVal.length() - 1) {
|
||||
String remainingPart = normalizeToPlaceholder(strVal.substring(endIndex + PLACEHOLDER_SUFFIX.length()));
|
||||
if (!Strings.isNullOrEmpty(remainingPart)) {
|
||||
stack.push(remainingPart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return placeholderKeys;
|
||||
}
|
||||
|
||||
private static String normalizeToPlaceholder(String strVal) {
|
||||
int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);
|
||||
if (startIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
int endIndex = strVal.lastIndexOf(PLACEHOLDER_SUFFIX);
|
||||
if (endIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return strVal.substring(startIndex, endIndex + PLACEHOLDER_SUFFIX.length());
|
||||
}
|
||||
|
||||
private static int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
|
||||
int index = startIndex + PLACEHOLDER_PREFIX.length();
|
||||
int withinNestedPlaceholder = 0;
|
||||
while (index < buf.length()) {
|
||||
if (StringUtils.substringMatch(buf, index, PLACEHOLDER_SUFFIX)) {
|
||||
if (withinNestedPlaceholder > 0) {
|
||||
withinNestedPlaceholder--;
|
||||
index = index + PLACEHOLDER_SUFFIX.length();
|
||||
} else {
|
||||
return index;
|
||||
}
|
||||
} else if (StringUtils.substringMatch(buf, index, SIMPLE_PLACEHOLDER_PREFIX)) {
|
||||
withinNestedPlaceholder++;
|
||||
index = index + SIMPLE_PLACEHOLDER_PREFIX.length();
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ public abstract class NativeDetector {
|
|||
private static final boolean imageCode = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
|
||||
|
||||
/**
|
||||
* Returns {@code true} if invoked in the context of image building or during image runtime, else {@code false}.
|
||||
* @return Returns {@code true} if invoked in the context of image building or during image runtime, else {@code false}.
|
||||
*/
|
||||
public static boolean inNativeImage() {
|
||||
return imageCode;
|
||||
|
|
|
@ -27,18 +27,14 @@ import org.yaml.snakeyaml.LoaderOptions;
|
|||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Tag;
|
||||
import org.yaml.snakeyaml.parser.ParserException;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
import org.yaml.snakeyaml.resolver.Resolver;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -59,17 +55,17 @@ public class DefaultYamlConfigParse extends AbstractConfigParse {
|
|||
}
|
||||
|
||||
protected static Yaml createYaml() {
|
||||
SafeConstructor constructor;
|
||||
if (getYamlAllowComplexObject()) {
|
||||
constructor = new Constructor();
|
||||
} else {
|
||||
constructor = new SafeConstructor();
|
||||
}
|
||||
Representer representer = new Representer();
|
||||
DumperOptions dumperOptions = new DumperOptions();
|
||||
LimitedResolver resolver = new LimitedResolver();
|
||||
LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setAllowDuplicateKeys(false);
|
||||
SafeConstructor constructor;
|
||||
if (getYamlAllowComplexObject()) {
|
||||
constructor = new Constructor(loaderOptions);
|
||||
} else {
|
||||
constructor = new SafeConstructor(loaderOptions);
|
||||
}
|
||||
DumperOptions dumperOptions = new DumperOptions();
|
||||
Representer representer = new Representer(dumperOptions);
|
||||
LimitedResolver resolver = new LimitedResolver();
|
||||
return new Yaml(constructor, representer, dumperOptions, loaderOptions, resolver);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package com.alibaba.nacos.spring.util;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author whq
|
||||
* @since 1.1.2
|
||||
*/
|
||||
public class PlaceholderHelperTest {
|
||||
|
||||
@Test
|
||||
public void testFindPlaceholderKeys() {
|
||||
/**
|
||||
* find keys from placeholder
|
||||
* ${key} => "key"
|
||||
* xxx${key}yyy => "key"
|
||||
* ${key:${key2:1}} => "key", "key2"
|
||||
* ${${key}} => "key"
|
||||
* ${${key:100}} => "key"
|
||||
* ${${key}:${key2}} => "key", "key2"
|
||||
*/
|
||||
Assert.assertEquals(PlaceholderHelper.findPlaceholderKeys("${key}"), Sets.newHashSet("key"));
|
||||
Assert.assertEquals(PlaceholderHelper.findPlaceholderKeys("xxx${key}yyy"), Sets.newHashSet("key"));
|
||||
Assert.assertEquals(PlaceholderHelper.findPlaceholderKeys("${key:${key2:1}}"), Sets.newHashSet("key", "key2"));
|
||||
Assert.assertEquals(PlaceholderHelper.findPlaceholderKeys("${${key}}"), Sets.newHashSet("key"));
|
||||
Assert.assertEquals(PlaceholderHelper.findPlaceholderKeys("${${key:100}}"), Sets.newHashSet("key"));
|
||||
Assert.assertEquals(PlaceholderHelper.findPlaceholderKeys("${${key}:${key2}}"), Sets.newHashSet("key", "key2"));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -15,34 +15,25 @@
|
|||
<name>Alibaba Nacos :: Samples :: Embedded Web Server</name>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>${nacos.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
|
@ -16,45 +16,34 @@
|
|||
<packaging>war</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>4.0.5.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>nacos-spring-context</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>nacos-embedded-webserver</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -20,20 +20,6 @@
|
|||
<module>nacos-spring-webmvc-sample</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<version>${spring.framework.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -46,6 +32,4 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
|
|
77
pom.xml
77
pom.xml
|
@ -51,14 +51,12 @@
|
|||
</mailingLists>
|
||||
|
||||
<developers>
|
||||
|
||||
<developer>
|
||||
<id>Alibaba Nacos</id>
|
||||
<name>Nacos</name>
|
||||
<url>http://nacos.io</url>
|
||||
<email>nacos_dev@linux.alibaba.com</email>
|
||||
</developer>
|
||||
|
||||
<developer>
|
||||
<id>mercyblitz</id>
|
||||
<name>小马哥</name>
|
||||
|
@ -76,7 +74,7 @@
|
|||
</licenses>
|
||||
|
||||
<properties>
|
||||
<revision>2.1.0-RC</revision>
|
||||
<revision>2.1.1</revision>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
@ -93,8 +91,10 @@
|
|||
<nacos.version>2.2.1</nacos.version>
|
||||
<!-- Spring -->
|
||||
<spring.framework.version>5.2.9.RELEASE</spring.framework.version>
|
||||
<logback-classic.version>1.2.9</logback-classic.version>
|
||||
<guava.version>32.0.0-jre</guava.version>
|
||||
<spring6.framework.version>6.0.8</spring6.framework.version>
|
||||
<snake.yaml.version>1.29</snake.yaml.version>
|
||||
<snakeyaml.version>1.29</snakeyaml.version>
|
||||
<!-- Servlet -->
|
||||
<servlet-api.version>3.0.1</servlet-api.version>
|
||||
<!-- javax-->
|
||||
|
@ -103,82 +103,118 @@
|
|||
<commons-lang3.version>3.4</commons-lang3.version>
|
||||
<!-- Alibaba's Spring Context Support -->
|
||||
<spring-context-support.version>1.0.11</spring-context-support.version>
|
||||
<jackson-core.version>2.13.4</jackson-core.version>
|
||||
<slf4j.version>1.7.7</slf4j.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
<mockito-core.version>2.15.0</mockito-core.version>
|
||||
</properties>
|
||||
|
||||
<!-- 管理依赖版本号,子项目不会默认依赖 -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${servlet-api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>${nacos.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Alibaba's Spring Context Support -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.spring</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<version>${spring-context-support.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
<version>${javax.annotation-api.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson-core.version}</version>
|
||||
</dependency>
|
||||
<!--yaml-->
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Framework -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>${spring.framework.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${spring.framework.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>${spring.framework.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>2.15.0</version>
|
||||
<version>${mockito-core.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<version>${spring.framework.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback-classic.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>nacos-spring-context</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>nacos-embedded-webserver</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
@ -282,7 +318,7 @@
|
|||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<version>1.6.0</version>
|
||||
<configuration>
|
||||
<updatePomFile>true</updatePomFile>
|
||||
<flattenMode>resolveCiFriendliesOnly</flattenMode>
|
||||
|
@ -362,6 +398,11 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
Loading…
Reference in New Issue