Merge branch '2021.0' of https://github.com/Tencent/spring-cloud-tencent into optimize/optimize-omponent-annotation

pull/672/head
wulingxiao 3 years ago
commit 3ffb3947ad

@ -1,34 +0,0 @@
name: Codecov
on:
push:
branches:
- hoxton
- 2021.0
- 2020.0
- greenwich
pull_request:
branches:
- hoxton
- 2021.0
- 2020.0
- greenwich
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout codes
uses: actions/checkout@v3
- name: Set up JDK 8
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 8
- name: Test with Maven
run: mvn -B test --file pom.xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ${{ github.workspace }}/target/site/jacoco/jacoco.xml

@ -1,22 +1,4 @@
# Change Log
---
- [Fix issue 593:When the project depends on spring-retry, executing restTemplate to call microservice and reporting NPE error](https://github.com/Tencent/spring-cloud-tencent/pull/594)
- [Optimize: remove discovery module useless code](https://github.com/Tencent/spring-cloud-tencent/pull/595)
- [Optimize: remove useless code for rest template router](https://github.com/Tencent/spring-cloud-tencent/pull/601)
- [Optimize: optimize configuration conditional & optimize config data tips](https://github.com/Tencent/spring-cloud-tencent/pull/603)
- [Automatically transmit some headers specified by an environment variable that directly defines header keys.](https://github.com/Tencent/spring-cloud-tencent/pull/606)
- [Optimize: Maybe remove Chinese characters](https://github.com/Tencent/spring-cloud-tencent/pull/607)
- [Optimize: InstanceId of PolarisRegistration and PolarisServiceRegistry](https://github.com/Tencent/spring-cloud-tencent/pull/610)
- [ fix pr #606 cause ci fail](https://github.com/Tencent/spring-cloud-tencent/pull/613)
- [fix pr 613: modify a judgment logic](https://github.com/Tencent/spring-cloud-tencent/pull/618)
- [fix pr 606: modify a part of changes requested by review.](https://github.com/Tencent/spring-cloud-tencent/pull/620)
- [Bugfix: fix feign report call result error when using feign direct call](https://github.com/Tencent/spring-cloud-tencent/pull/621)
- [remove useless code for router](https://github.com/Tencent/spring-cloud-tencent/pull/626)
- [Feature: support new label expression](https://github.com/Tencent/spring-cloud-tencent/pull/627)
- [feat:report the labels when using RestTemplate.](https://github.com/Tencent/spring-cloud-tencent/pull/629)
- [Bugfix: fix throw npe when router context is null](https://github.com/Tencent/spring-cloud-tencent/pull/635)
- [Optimize:optimize transfer](https://github.com/Tencent/spring-cloud-tencent/pull/636)
- [fix:cancel reporting useless metadata.](https://github.com/Tencent/spring-cloud-tencent/pull/639)
- [Feature: support read config file from local file system](https://github.com/Tencent/spring-cloud-tencent/pull/640)
- [fix:optimize expression parser V1.](https://github.com/Tencent/spring-cloud-tencent/pull/642)
- [Optimize:optimize SpringValueProcessor.](https://github.com/Tencent/spring-cloud-tencent/pull/655)

@ -17,9 +17,11 @@
Spring Cloud Tencent 是腾讯开源的一站式微服务解决方案。
Spring Cloud Tencent 实现了Spring Cloud 标准微服务 SPI开发者可以基于 Spring Cloud Tencent 快速开发 Spring Cloud 云原生分布式应用。
Spring Cloud Tencent 实现了Spring Cloud 标准微服务 SPI开发者可以基于 Spring Cloud Tencent 快速开发 Spring Cloud
云原生分布式应用。
Spring Cloud Tencent 的核心依托腾讯开源的一站式服务发现与治理平台 [Polaris](https://github.com/polarismesh/polaris),实现各种分布式微服务场景。
Spring Cloud Tencent 的核心依托腾讯开源的一站式服务发现与治理平台 [Polaris](https://github.com/polarismesh/polaris)
,实现各种分布式微服务场景。
- [Polaris Github home page](https://github.com/polarismesh/polaris)
- [Polaris official website](https://polarismesh.cn/)
@ -45,12 +47,13 @@ Spring Cloud Tencent提供的能力包括但不限于
- 控制面地址: `grpc://183.47.111.80:8091`
-
`spring-cloud-tencent-example` 下 example 地址都默认指向了体验服务地址(`grpc://183.47.111.80:8091`),如果您只是体验 Spring Cloud
`spring-cloud-tencent-example` 下 example 地址都默认指向了体验服务地址(`grpc://183.47.111.80:8091`),如果您只是体验
Spring Cloud
Tencent可直接一键运行任何 example。
## 管控台
<img width="1792" alt="image" src="https://user-images.githubusercontent.com/4991116/163402268-48493802-4555-4b93-8e31-011410f2166b.png">
<img width="1727" alt="sc" src="https://user-images.githubusercontent.com/4991116/197529819-78b20ba8-0e60-450c-a8e3-0c2bf04caa15.png">
## 使用指南
@ -58,7 +61,10 @@ Spring Cloud Tencent 所有组件都已上传到 Maven 中央仓库,只需要
> 注意:
>
> Spring Cloud Tencent 的版本列表可以查看 [Spring Cloud Tencent 版本管理](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86) 。
> Spring Cloud Tencent
>
的版本列表可以查看 [Spring Cloud Tencent 版本管理](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86)
> 。
例如:
@ -70,7 +76,7 @@ Spring Cloud Tencent 所有组件都已上传到 Maven 中央仓库,只需要
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-dependencies</artifactId>
<!--version number-->
<version>1.7.0-2021.0.3</version>
<version>1.8.1-2021.0.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>

@ -51,7 +51,7 @@ directly with one click.
## Screenshots
<img width="1792" alt="image" src="https://user-images.githubusercontent.com/4991116/163402268-48493802-4555-4b93-8e31-011410f2166b.png">
<img width="1727" alt="sc" src="https://user-images.githubusercontent.com/4991116/197529819-78b20ba8-0e60-450c-a8e3-0c2bf04caa15.png">
## Use Guide
@ -60,7 +60,10 @@ dependencies.
> Notice:
>
> The version list of Spring Cloud Tencent can be found in [Spring Cloud Tencent Version Management](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86).
> The version list of Spring Cloud Tencent can be found
>
in [Spring Cloud Tencent Version Management](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86)
> .
For example:
@ -72,7 +75,7 @@ For example:
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-dependencies</artifactId>
<!--version number-->
<version>1.7.0-2021.0.3</version>
<version>1.8.1-2021.0.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>

@ -0,0 +1,24 @@
# Change Log
---
- [Fix issue 593:When the project depends on spring-retry, executing restTemplate to call microservice and reporting NPE error](https://github.com/Tencent/spring-cloud-tencent/pull/594)
- [Optimize: remove discovery module useless code](https://github.com/Tencent/spring-cloud-tencent/pull/595)
- [Optimize: remove useless code for rest template router](https://github.com/Tencent/spring-cloud-tencent/pull/601)
- [Optimize: optimize configuration conditional & optimize config data tips](https://github.com/Tencent/spring-cloud-tencent/pull/603)
- [Automatically transmit some headers specified by an environment variable that directly defines header keys.](https://github.com/Tencent/spring-cloud-tencent/pull/606)
- [Optimize: Maybe remove Chinese characters](https://github.com/Tencent/spring-cloud-tencent/pull/607)
- [Optimize: InstanceId of PolarisRegistration and PolarisServiceRegistry](https://github.com/Tencent/spring-cloud-tencent/pull/610)
- [ fix pr #606 cause ci fail](https://github.com/Tencent/spring-cloud-tencent/pull/613)
- [fix pr 613: modify a judgment logic](https://github.com/Tencent/spring-cloud-tencent/pull/618)
- [fix pr 606: modify a part of changes requested by review.](https://github.com/Tencent/spring-cloud-tencent/pull/620)
- [Bugfix: fix feign report call result error when using feign direct call](https://github.com/Tencent/spring-cloud-tencent/pull/621)
- [remove useless code for router](https://github.com/Tencent/spring-cloud-tencent/pull/626)
- [Feature: support new label expression](https://github.com/Tencent/spring-cloud-tencent/pull/627)
- [feat:report the labels when using RestTemplate.](https://github.com/Tencent/spring-cloud-tencent/pull/629)
- [Bugfix: fix throw npe when router context is null](https://github.com/Tencent/spring-cloud-tencent/pull/635)
- [Optimize:optimize transfer](https://github.com/Tencent/spring-cloud-tencent/pull/636)
- [fix:cancel reporting useless metadata.](https://github.com/Tencent/spring-cloud-tencent/pull/639)
- [Feature: support read config file from local file system](https://github.com/Tencent/spring-cloud-tencent/pull/640)
- [fix:optimize expression parser V1.](https://github.com/Tencent/spring-cloud-tencent/pull/642)
- [Optimize: add transfer metadata unit test.](https://github.com/Tencent/spring-cloud-tencent/pull/646)
- [feat: publish spring event named ConfigChangeSpringEvent when the configuration is changed](https://github.com/Tencent/spring-cloud-tencent/pull/647)

@ -0,0 +1,4 @@
# Change Log
---
- [fix:fix transfer http headers not working bug.](https://github.com/Tencent/spring-cloud-tencent/pull/664)

@ -89,7 +89,7 @@
<properties>
<!-- Project revision -->
<revision>1.8.0-2021.0.3-SNAPSHOT</revision>
<revision>1.9.0-2021.0.3-SNAPSHOT</revision>
<!-- Spring Cloud -->
<spring.cloud.version>2021.0.3</spring.cloud.version>

@ -60,7 +60,7 @@ public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor,
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> transHeaders = metadataContext.getTransHeaders();
Map<String, String> transHeaders = metadataContext.getTransHeadersKV();
this.buildMetadataHeader(requestTemplate, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);

@ -60,7 +60,7 @@ public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRe
MetadataContext metadataContext = MetadataContextHolder.get();
Map<String, String> customMetadata = metadataContext.getCustomMetadata();
Map<String, String> disposableMetadata = metadataContext.getDisposableMetadata();
Map<String, String> transHeaders = metadataContext.getTransHeaders();
Map<String, String> transHeaders = metadataContext.getTransHeadersKV();
// build custom disposable metadata request header
this.buildMetadataHeader(httpRequest, disposableMetadata, CUSTOM_DISPOSABLE_METADATA);

@ -0,0 +1,86 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.tencent.cloud.metadata.core;
import java.util.Map;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
import org.junit.AfterClass;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* Test for {@link TransHeadersTransfer}.
*
* @author lingxiao.wlx
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT,
classes = DecodeTransferMetadataServletFilterTest.TestApplication.class,
properties = {"spring.config.location = classpath:application-test.yml"})
public class TransHeadersTransferTest {
@AfterClass
public static void afterClass() {
MetadataContextHolder.remove();
}
@Test
public void transferServletTest() {
MetadataContext metadataContext = MetadataContextHolder.get();
metadataContext.setTransHeaders("header1,header2,header3", "");
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("header1", "1");
request.addHeader("header2", "2");
request.addHeader("header3", "3");
TransHeadersTransfer.transfer(request);
Map<String, String> transHeadersKV = MetadataContextHolder.get().getTransHeadersKV();
Assertions.assertEquals(transHeadersKV.get("header1"), "1");
Assertions.assertEquals(transHeadersKV.get("header2"), "2");
Assertions.assertEquals(transHeadersKV.get("header3"), "3");
}
@Test
public void transferReactiveTest() {
MetadataContext metadataContext = MetadataContextHolder.get();
metadataContext.setTransHeaders("header1,header2,header3", "");
MockServerHttpRequest.BaseBuilder<?> builder = MockServerHttpRequest.get("");
String[] header1 = {"1"};
String[] header2 = {"2"};
String[] header3 = {"3"};
builder.header("header1", header1);
builder.header("header2", header2);
builder.header("header3", header3);
MockServerHttpRequest request = builder.build();
TransHeadersTransfer.transfer(request);
Map<String, String> transHeadersKV = MetadataContextHolder.get().getTransHeadersKV();
Assertions.assertEquals(transHeadersKV.get("header1"), JacksonUtils.serialize2Json(header1));
Assertions.assertEquals(transHeadersKV.get("header2"), JacksonUtils.serialize2Json(header2));
Assertions.assertEquals(transHeadersKV.get("header3"), JacksonUtils.serialize2Json(header3));
}
}

@ -23,11 +23,14 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import com.google.common.collect.Maps;
import com.tencent.cloud.polaris.config.spring.event.ConfigChangeSpringEvent;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
@ -44,10 +47,12 @@ import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerCon
*
* @author <a href="mailto:iskp.me@gmail.com">Elve.Xu</a> 2022-06-08
*/
public final class PolarisConfigChangeEventListener implements ApplicationListener<ApplicationEvent> {
public final class PolarisConfigChangeEventListener implements ApplicationListener<ApplicationEvent>, ApplicationEventPublisherAware {
private static final AtomicBoolean started = new AtomicBoolean();
private ApplicationEventPublisher eventPublisher;
/**
* Handle an application event.
*
@ -72,6 +77,8 @@ public final class PolarisConfigChangeEventListener implements ApplicationListen
ConfigurableEnvironment environment = context.getEnvironment();
Map<String, Object> ret = loadEnvironmentProperties(environment);
Map<String, ConfigPropertyChangeInfo> changes = merge(ret);
ConfigChangeSpringEvent configChangeSpringEvent = new ConfigChangeSpringEvent(Maps.newHashMap(changes));
eventPublisher.publishEvent(configChangeSpringEvent);
fireConfigChange(changes.keySet(), Maps.newHashMap(changes));
changes.clear();
}
@ -106,4 +113,9 @@ public final class PolarisConfigChangeEventListener implements ApplicationListen
});
return ret;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
}

@ -22,27 +22,34 @@ import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper;
import com.tencent.cloud.polaris.config.spring.property.SpringValue;
import com.tencent.cloud.polaris.config.spring.property.SpringValueDefinition;
import com.tencent.cloud.polaris.config.spring.property.SpringValueDefinitionProcessor;
import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.NonNull;
@ -55,10 +62,13 @@ import org.springframework.lang.NonNull;
*
* @author weihubeats 2022-7-10
*/
public class SpringValueProcessor extends PolarisProcessor implements BeanFactoryPostProcessor, BeanFactoryAware {
public class SpringValueProcessor extends PolarisProcessor implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringValueProcessor.class);
private static final Set<BeanDefinitionRegistry> PROPERTY_VALUES_PROCESSED_BEAN_FACTORIES = Sets.newConcurrentHashSet();
private static final Map<BeanDefinitionRegistry, Multimap<String, SpringValueDefinition>> BEAN_DEFINITION_REGISTRY_MULTIMAP_CONCURRENT_MAP =
Maps.newConcurrentMap();
private final PolarisConfigProperties polarisConfigProperties;
private final PlaceholderHelper placeholderHelper;
private final SpringValueRegistry springValueRegistry;
@ -79,8 +89,7 @@ public class SpringValueProcessor extends PolarisProcessor implements BeanFactor
public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory)
throws BeansException {
if (polarisConfigProperties.isAutoRefresh() && beanFactory instanceof BeanDefinitionRegistry) {
beanName2SpringValueDefinitions = SpringValueDefinitionProcessor
.getBeanName2SpringValueDefinitions((BeanDefinitionRegistry) beanFactory);
beanName2SpringValueDefinitions = this.getBeanName2SpringValueDefinitions((BeanDefinitionRegistry) beanFactory);
}
}
@ -126,6 +135,18 @@ public class SpringValueProcessor extends PolarisProcessor implements BeanFactor
doRegister(bean, beanName, method, value);
}
@Override
public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void postProcessBeanDefinitionRegistry(@NonNull BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
if (polarisConfigProperties.isAutoRefresh()) {
processPropertyValues(beanDefinitionRegistry);
}
}
private void doRegister(Object bean, String beanName, Member member, Value value) {
Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
if (keys.isEmpty()) {
@ -182,8 +203,47 @@ public class SpringValueProcessor extends PolarisProcessor implements BeanFactor
beanName2SpringValueDefinitions.removeAll(beanName);
}
@Override
public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
private Multimap<String, SpringValueDefinition> getBeanName2SpringValueDefinitions(BeanDefinitionRegistry registry) {
Multimap<String, SpringValueDefinition> springValueDefinitions = BEAN_DEFINITION_REGISTRY_MULTIMAP_CONCURRENT_MAP.remove(registry);
if (springValueDefinitions == null) {
springValueDefinitions = LinkedListMultimap.create();
}
return springValueDefinitions;
}
private void processPropertyValues(BeanDefinitionRegistry beanRegistry) {
if (!PROPERTY_VALUES_PROCESSED_BEAN_FACTORIES.add(beanRegistry)) {
// already initialized
return;
}
if (!BEAN_DEFINITION_REGISTRY_MULTIMAP_CONCURRENT_MAP.containsKey(beanRegistry)) {
BEAN_DEFINITION_REGISTRY_MULTIMAP_CONCURRENT_MAP.put(beanRegistry, LinkedListMultimap.create());
}
Multimap<String, SpringValueDefinition> springValueDefinitions = BEAN_DEFINITION_REGISTRY_MULTIMAP_CONCURRENT_MAP.get(beanRegistry);
String[] beanNames = beanRegistry.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanRegistry.getBeanDefinition(beanName);
MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues();
List<PropertyValue> propertyValues = mutablePropertyValues.getPropertyValueList();
for (PropertyValue propertyValue : propertyValues) {
Object value = propertyValue.getValue();
if (!(value instanceof TypedStringValue)) {
continue;
}
String placeholder = ((TypedStringValue) value).getValue();
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeholder);
if (keys.isEmpty()) {
continue;
}
for (String key : keys) {
springValueDefinitions.put(beanName, new SpringValueDefinition(key, placeholder, propertyValue.getName()));
}
}
}
}
}

@ -0,0 +1,70 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.tencent.cloud.polaris.config.spring.event;
import java.util.Map;
import java.util.Set;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.springframework.context.ApplicationEvent;
/**
* A spring change event when config is changed.
*
* @author Derek Yi 2022-10-16
*/
public class ConfigChangeSpringEvent extends ApplicationEvent {
/**
* @param source all changed keys map.
*/
public ConfigChangeSpringEvent(Map<String, ConfigPropertyChangeInfo> source) {
super(source);
}
/**
* Get the changed keys.
* @return the list of the keys
*/
public Set<String> changedKeys() {
return changeMap().keySet();
}
/**
* Get a specific change instance for the key specified.
* @param key the changed key
* @return the change instance
*/
public ConfigPropertyChangeInfo getChange(String key) {
return changeMap().get(key);
}
/**
* Check whether the specified key is changed .
* @param key the key
* @return true if the key is changed, false otherwise.
*/
public boolean isChanged(String key) {
return changeMap().containsKey(key);
}
private Map<String, ConfigPropertyChangeInfo> changeMap() {
return (Map<String, ConfigPropertyChangeInfo>) getSource();
}
}

@ -1,126 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.tencent.cloud.polaris.config.spring.property;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.lang.NonNull;
/**
* To process xml config placeholders, e.g.
*
* <pre>
* &lt;bean class=&quot;com.demo.bean.XmlBean&quot;&gt;
* &lt;property name=&quot;timeout&quot; value=&quot;${timeout:200}&quot;/&gt;
* &lt;property name=&quot;batch&quot; value=&quot;${batch:100}&quot;/&gt;
* &lt;/bean&gt;
* </pre>
*
* This source file was originally from:
* <code><a href=https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/property/SpringValueDefinitionProcessor.java>
* SpringValueDefinitionProcessor</a></code>
*
* @author weihubeats 2022-7-10
*/
public class SpringValueDefinitionProcessor implements BeanDefinitionRegistryPostProcessor {
private static final Map<BeanDefinitionRegistry, Multimap<String, SpringValueDefinition>> beanName2SpringValueDefinitions =
Maps.newConcurrentMap();
private static final Set<BeanDefinitionRegistry> PROPERTY_VALUES_PROCESSED_BEAN_FACTORIES = Sets.newConcurrentHashSet();
private final PlaceholderHelper placeholderHelper;
private final PolarisConfigProperties polarisConfigProperties;
public SpringValueDefinitionProcessor(PlaceholderHelper placeholderHelper, PolarisConfigProperties polarisConfigProperties) {
this.polarisConfigProperties = polarisConfigProperties;
this.placeholderHelper = placeholderHelper;
}
public static Multimap<String, SpringValueDefinition> getBeanName2SpringValueDefinitions(BeanDefinitionRegistry registry) {
Multimap<String, SpringValueDefinition> springValueDefinitions = beanName2SpringValueDefinitions.get(registry);
if (springValueDefinitions == null) {
springValueDefinitions = LinkedListMultimap.create();
}
return springValueDefinitions;
}
@Override
public void postProcessBeanDefinitionRegistry(@NonNull BeanDefinitionRegistry registry) throws BeansException {
if (polarisConfigProperties.isAutoRefresh()) {
processPropertyValues(registry);
}
}
@Override
public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
private void processPropertyValues(BeanDefinitionRegistry beanRegistry) {
if (!PROPERTY_VALUES_PROCESSED_BEAN_FACTORIES.add(beanRegistry)) {
// already initialized
return;
}
if (!beanName2SpringValueDefinitions.containsKey(beanRegistry)) {
beanName2SpringValueDefinitions.put(beanRegistry, LinkedListMultimap.create());
}
Multimap<String, SpringValueDefinition> springValueDefinitions = beanName2SpringValueDefinitions.get(beanRegistry);
String[] beanNames = beanRegistry.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanRegistry.getBeanDefinition(beanName);
MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues();
List<PropertyValue> propertyValues = mutablePropertyValues.getPropertyValueList();
for (PropertyValue propertyValue : propertyValues) {
Object value = propertyValue.getValue();
if (!(value instanceof TypedStringValue)) {
continue;
}
String placeholder = ((TypedStringValue) value).getValue();
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeholder);
if (keys.isEmpty()) {
continue;
}
for (String key : keys) {
springValueDefinitions.put(beanName, new SpringValueDefinition(key, placeholder, propertyValue.getName()));
}
}
}
}
}

@ -27,6 +27,7 @@ import java.util.Optional;
import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration;
import com.tencent.cloud.polaris.config.enums.RefreshType;
import com.tencent.cloud.polaris.config.spring.property.Person;
import com.tencent.cloud.polaris.config.spring.property.SpringValue;
import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
import com.tencent.polaris.api.utils.CollectionUtils;
@ -43,6 +44,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.stereotype.Component;
/**
@ -138,6 +140,53 @@ public class SpringValueProcessorTest {
});
}
@Test
public void xmlBeamDefinitionTest() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(XMLBeamDefinitionTest.class))
.withConfiguration(AutoConfigurations.of(PolarisConfigAutoConfiguration.class))
.withPropertyValues("spring.application.name=" + "conditionalOnConfigReflectEnabledTest")
.withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081")
.withPropertyValues("spring.cloud.polaris.config.refresh-type=" + RefreshType.REFLECT)
.withPropertyValues("spring.cloud.polaris.config.enabled=true")
.withPropertyValues("name=test");
contextRunner.run(context -> {
Person person = context.getBean(Person.class);
SpringValueRegistry springValueRegistry = context.getBean(SpringValueRegistry.class);
BeanFactory beanFactory = person.getBeanFactory();
Collection<SpringValue> name = springValueRegistry.get(beanFactory, "name");
Assert.assertFalse(CollectionUtils.isEmpty(name));
Optional<SpringValue> nameSpringValueOptional = name.stream().findAny();
Assert.assertTrue(nameSpringValueOptional.isPresent());
SpringValue nameSpringValue = nameSpringValueOptional.get();
Method method = nameSpringValue.getMethodParameter().getMethod();
Assert.assertTrue(Objects.nonNull(method));
Assert.assertEquals("setName", method.getName());
Assert.assertEquals("${name:test}", nameSpringValue.getPlaceholder());
Assert.assertFalse(nameSpringValue.isField());
Assert.assertEquals(String.class, nameSpringValue.getTargetType());
Collection<SpringValue> age = springValueRegistry.get(beanFactory, "age");
Assert.assertFalse(CollectionUtils.isEmpty(age));
Optional<SpringValue> ageSpringValueOptional = age.stream().findAny();
Assert.assertTrue(ageSpringValueOptional.isPresent());
SpringValue ageSpringValue = ageSpringValueOptional.get();
Method method1 = ageSpringValue.getMethodParameter().getMethod();
Assert.assertTrue(Objects.nonNull(method1));
Assert.assertEquals("setAge", method1.getName());
Assert.assertEquals("${age:10}", ageSpringValue.getPlaceholder());
Assert.assertFalse(ageSpringValue.isField());
Assert.assertEquals(String.class, ageSpringValue.getTargetType());
});
}
@Configuration
@EnableAutoConfiguration
static class PolarisConfigAutoConfiguration {
@ -173,4 +222,9 @@ public class SpringValueProcessorTest {
ValueTest.name = name;
}
}
@Configuration
@ImportResource("classpath:bean.xml")
static class XMLBeamDefinitionTest {
}
}

@ -0,0 +1,80 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.tencent.cloud.polaris.config.spring.event;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.tencent.polaris.configuration.api.core.ChangeType;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
/**
* Test for {@link ConfigChangeSpringEvent}.
*
* @author derek.yi 2022-10-16
*/
public class ConfigChangeSpringEventTest {
private static CountDownLatch countDownLatch = new CountDownLatch(1);
private static AtomicInteger receiveEventTimes = new AtomicInteger();
@Test
public void testPublishConfigChangeSpringEvent() {
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(ConfigChangeSpringEventListener.class));
contextRunner.run(context -> {
HashMap<String, ConfigPropertyChangeInfo> changeMap = new HashMap<>();
changeMap.put("key", new ConfigPropertyChangeInfo("key", null, "value", ChangeType.ADDED));
context.publishEvent(new ConfigChangeSpringEvent(changeMap));
countDownLatch.await(5, TimeUnit.SECONDS);
Assert.assertEquals(1, receiveEventTimes.get());
});
}
@Configuration
static class ConfigChangeSpringEventListener implements ApplicationListener<ConfigChangeSpringEvent> {
@Override
public void onApplicationEvent(ConfigChangeSpringEvent event) {
Set<String> changedKeys = event.changedKeys();
Assert.assertEquals(1, changedKeys.size());
Assert.assertTrue(event.isChanged("key"));
ConfigPropertyChangeInfo changeInfo = event.getChange("key");
Assert.assertNotNull(changeInfo);
Assert.assertEquals("key", changeInfo.getPropertyName());
Assert.assertEquals("value", changeInfo.getNewValue());
Assert.assertEquals(ChangeType.ADDED, changeInfo.getChangeType());
receiveEventTimes.incrementAndGet();
countDownLatch.countDown();
}
}
}

@ -1,75 +0,0 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.tencent.cloud.polaris.config.spring.property;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import com.tencent.polaris.api.utils.CollectionUtils;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Test for {@link SpringValueDefinitionProcessor}.
*
* @author lingxiao.wlx
*/
public class SpringValueDefinitionProcessorTest {
@Test
public void springValueDefinitionProcessorTest() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Person person = context.getBean(Person.class);
SpringValueRegistry springValueRegistry = context.getBean(SpringValueRegistry.class);
BeanFactory beanFactory = person.getBeanFactory();
Collection<SpringValue> name = springValueRegistry.get(beanFactory, "name");
Assert.assertFalse(CollectionUtils.isEmpty(name));
Optional<SpringValue> nameSpringValueOptional = name.stream().findAny();
Assert.assertTrue(nameSpringValueOptional.isPresent());
SpringValue nameSpringValue = nameSpringValueOptional.get();
Method method = nameSpringValue.getMethodParameter().getMethod();
Assert.assertTrue(Objects.nonNull(method));
Assert.assertEquals("setName", method.getName());
Assert.assertEquals("${name:test}", nameSpringValue.getPlaceholder());
Assert.assertFalse(nameSpringValue.isField());
Assert.assertEquals(String.class, nameSpringValue.getTargetType());
Collection<SpringValue> age = springValueRegistry.get(beanFactory, "age");
Assert.assertFalse(CollectionUtils.isEmpty(age));
Optional<SpringValue> ageSpringValueOptional = age.stream().findAny();
Assert.assertTrue(ageSpringValueOptional.isPresent());
SpringValue ageSpringValue = ageSpringValueOptional.get();
Method method1 = ageSpringValue.getMethodParameter().getMethod();
Assert.assertTrue(Objects.nonNull(method1));
Assert.assertEquals("setAge", method1.getName());
Assert.assertEquals("${age:10}", ageSpringValue.getPlaceholder());
Assert.assertFalse(ageSpringValue.isField());
Assert.assertEquals(String.class, ageSpringValue.getTargetType());
}
}

@ -11,28 +11,4 @@
<property name="name" value="${name:test}"/>
<property name="age" value="${age:10}"/>
</bean>
<!--SpringValueDefinitionProcessor to process xml config placeholders -->
<bean class="com.tencent.cloud.polaris.config.spring.property.SpringValueDefinitionProcessor">
<constructor-arg index="0" ref="helper"/>
<constructor-arg index="1" ref="polarisConfigProperties"/>
</bean>
<!--Placeholder helper functions -->
<bean id="helper" class="com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper"/>
<!--PolarisConfigProperties -->
<bean id="polarisConfigProperties" class="com.tencent.cloud.polaris.config.config.PolarisConfigProperties">
<property name="autoRefresh" value="true"/>
</bean>
<!--springValueRegistry -->
<bean id="springValueRegistry" class="com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry"/>
<!--Spring value processor of method -->
<bean class="com.tencent.cloud.polaris.config.spring.annotation.SpringValueProcessor">
<constructor-arg index="0" ref="helper"/>
<constructor-arg index="1" ref="springValueRegistry"/>
<constructor-arg index="2" ref="polarisConfigProperties"/>
</bean>
</beans>

@ -70,7 +70,7 @@
</developers>
<properties>
<revision>1.8.0-2021.0.3-SNAPSHOT</revision>
<revision>1.9.0-2021.0.3-SNAPSHOT</revision>
<!-- Dependencies -->
<polaris.version>1.9.1</polaris.version>

@ -34,6 +34,11 @@
<artifactId>spring-cloud-tencent-gateway-plugin</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-featureenv-plugin</artifactId>

Loading…
Cancel
Save