Compare commits

..

11 Commits

Author SHA1 Message Date
W ce41f4d407
修复项目启动注入属性为null的空指针问题 (#357) 2025-06-09 19:59:46 +08:00
胡俊 bede11a7e2
update version 2.1.1 (#348) 2024-08-01 14:43:27 +08:00
abin 9388aa7f65
use nacos-spring-context self AbstractAnnotationBeanPostProcessor (#345) (#346) 2024-07-27 16:36:45 +08:00
Jefferson Chern 4b106fb992
resolve javadoc error. (#344)
* resolve javadoc error.

* add maven-war-plugin
2024-06-14 19:31:29 +08:00
胡俊 9b61ef68d2
update version 2.1.1-RC (#343) 2024-06-04 22:17:35 +08:00
Jefferson Chern 0a75c0b8c0
# 1.add guava in nacos-spring-context; (#342)
# 2.refactor dependency version.
2024-06-04 22:08:24 +08:00
W eb8b10f3f3
[ISSUE #285] Fix 服务端配置修改后,不支持@Value值的动态变更 #285 (#286)
* fix the thread-safety problem in NacosConfigurationPropertiesBinder (#268)

* 新增服务端配置更改后spring注解@Value值动态变更功能

---------

Co-authored-by: realJackSun <sjtusl@163.com>
Co-authored-by: 胡俊 <510830970@qq.com>
2024-05-09 23:27:48 +08:00
codestagea b209b291c2
修复 配置项value $符号异常 #293 (#317)
Co-authored-by: huijun.xu <huijun.xu@urbanic.com>
Co-authored-by: 胡俊 <510830970@qq.com>
2024-05-09 23:20:00 +08:00
pdkst fcf008405b
improve snakeyml compatibility for 2.2 (#336) 2024-05-09 23:11:27 +08:00
jsbxyyx 1ada9f4e88
fix: empty yaml npe (#332) 2023-12-18 20:55:36 +08:00
SuperZ1999 b1351d7f88
Summer (#331)
* [ISSUE #321] Support spring 6.x (#324)

* [ISSUE #315] Support spring 6.x

* Update document (#325)

* Modify version

---------

Co-authored-by: hujun3 <hujun3@xiaomi.com>
2023-12-03 13:05:31 +08:00
18 changed files with 604 additions and 115 deletions

View File

@ -30,9 +30,7 @@
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-spring-context</artifactId>
<version>${revision}</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -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>
</project>

View File

@ -406,8 +406,10 @@ public abstract class AbstractAnnotationBeanPostProcessor implements Instantiati
if (injectedObject == null) {
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
// Customized inject-object if necessary
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
if (injectedObject != null) {
// Customized inject-object if necessary
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
}
return injectedObject;
@ -639,4 +641,4 @@ public abstract class AbstractAnnotationBeanPostProcessor implements Instantiati
}
}
}
}
}

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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)) {

View File

@ -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(

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
@ -49,30 +45,30 @@ import java.util.regex.Pattern;
* @since 0.3.0
*/
public class DefaultYamlConfigParse extends AbstractConfigParse {
protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultYamlConfigParse.class);
private static final String YAML_ALLOW_COMPLEX_OBJECT = "yamlAllowComplexObject";
private static boolean getYamlAllowComplexObject() {
return Boolean.getBoolean(YAML_ALLOW_COMPLEX_OBJECT);
}
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);
}
protected static boolean process(MatchCallback callback, Yaml yaml, String content) {
int count = 0;
if (LOGGER.isDebugEnabled()) {
@ -88,7 +84,7 @@ public class DefaultYamlConfigParse extends AbstractConfigParse {
}
return (count > 0);
}
protected static boolean process(Map<String, Object> map, MatchCallback callback) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Merging document (no matchers set): " + map);
@ -96,7 +92,7 @@ public class DefaultYamlConfigParse extends AbstractConfigParse {
callback.process(getFlattenedMap(map));
return true;
}
@SuppressWarnings("unchecked")
protected static Map<String, Object> asMap(Object object) {
// YAML can have numbers as keys
@ -106,7 +102,7 @@ public class DefaultYamlConfigParse extends AbstractConfigParse {
result.put("document", object);
return result;
}
Map<Object, Object> map = (Map<Object, Object>) object;
for (Map.Entry<Object, Object> entry : map.entrySet()) {
Object key = entry.getKey();
@ -122,9 +118,9 @@ public class DefaultYamlConfigParse extends AbstractConfigParse {
}
return result;
}
private static class LimitedResolver extends Resolver {
@Override
public void addImplicitResolver(Tag tag, Pattern regexp, String first) {
if (tag == Tag.TIMESTAMP) {
@ -133,13 +129,13 @@ public class DefaultYamlConfigParse extends AbstractConfigParse {
super.addImplicitResolver(tag, regexp, first);
}
}
protected static Map<String, Object> getFlattenedMap(Map<String, Object> source) {
Map<String, Object> result = new LinkedHashMap<String, Object>();
buildFlattenedMap(result, source, null);
return result;
}
protected static void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, String path) {
for (Map.Entry<String, Object> entry : source.entrySet()) {
String key = entry.getKey();
@ -169,7 +165,7 @@ public class DefaultYamlConfigParse extends AbstractConfigParse {
}
}
}
@Override
public Map<String, Object> parse(String configText) {
final AtomicReference<Map<String, Object>> result = new AtomicReference<Map<String, Object>>();
@ -181,14 +177,14 @@ public class DefaultYamlConfigParse extends AbstractConfigParse {
}, createYaml(), configText);
return result.get();
}
@Override
public String processType() {
return ConfigType.YAML.getType();
}
protected interface MatchCallback {
/**
* Put Map to Properties.
*
@ -196,5 +192,5 @@ public class DefaultYamlConfigParse extends AbstractConfigParse {
*/
void process(Map<String, Object> map);
}
}

View File

@ -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"));
}
}

View File

@ -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>
</project>

View File

@ -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>

View File

@ -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
View File

@ -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</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>