From d4c22dd1969b6913070aa633330458535f265e6c Mon Sep 17 00:00:00 2001 From: Haotian Zhang <928016560@qq.com> Date: Wed, 31 Aug 2022 15:22:32 +0800 Subject: [PATCH] feature:add @ConditionalOnConfigReflectEnabled annotation. (#538) --- CHANGELOG.md | 1 + .../PolarisConfigAutoConfiguration.java | 72 +++--- ...larisConfigBootstrapAutoConfiguration.java | 12 +- ...fectedConfigurationPropertiesRebinder.java | 82 +++++++ .../PolarisConfigPropertyAutoRefresher.java | 116 ++++++++++ .../PolarisConfigPropertyRefresher.java | 44 ++++ .../PolarisPropertySourceAutoRefresher.java | 217 ------------------ ...olarisRefreshAffectedContextRefresher.java | 123 ++++++++++ .../PolarisRefreshEntireContextRefresher.java | 53 +++++ .../SmartConfigurationPropertiesRebinder.java | 122 ---------- ...a => ConditionalOnReflectRefreshType.java} | 4 +- ....java => ReflectRefreshTypeCondition.java} | 27 +-- .../spring/annotation/PolarisProcessor.java | 15 +- .../annotation/SpringValueProcessor.java | 3 +- .../spring/property/PlaceholderHelper.java | 9 +- .../config/spring/property/SpringValue.java | 11 +- ...itional-spring-configuration-metadata.json | 12 + ...arisPropertiesSourceAutoRefresherTest.java | 12 +- .../ConditionalOnReflectRefreshTypeTest.java | 86 +++++++ .../annotation/SpringValueProcessorTest.java | 153 ++++++++++++ .../config/spring/property/Person.java | 67 ++++++ .../property/PlaceholderHelperTest.java | 88 +++++++ .../SpringValueDefinitionProcessorTest.java | 75 ++++++ .../resources/application-test.properties | 2 + .../src/test/resources/bean.xml | 38 +++ .../src/main/resources/bootstrap.yml | 3 +- 26 files changed, 1029 insertions(+), 418 deletions(-) create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/AffectedConfigurationPropertiesRebinder.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyRefresher.java delete mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySourceAutoRefresher.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshAffectedContextRefresher.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresher.java delete mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/SmartConfigurationPropertiesRebinder.java rename spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/{ConditionalOnNonDefaultBehavior.java => ConditionalOnReflectRefreshType.java} (92%) rename spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/{NonDefaultBehaviorCondition.java => ReflectRefreshTypeCondition.java} (65%) create mode 100644 spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/condition/ConditionalOnReflectRefreshTypeTest.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessorTest.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/Person.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/PlaceholderHelperTest.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/SpringValueDefinitionProcessorTest.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/test/resources/application-test.properties create mode 100644 spring-cloud-starter-tencent-polaris-config/src/test/resources/bean.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index ab8d197f7..2378e7698 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,4 +25,5 @@ - [Feature: remove location metadata](https://github.com/Tencent/spring-cloud-tencent/pull/534) - [Feature: remove location metadata](https://github.com/Tencent/spring-cloud-tencent/pull/540) - [add feature-env plugin & add spring cloud gateway staining plugin](https://github.com/Tencent/spring-cloud-tencent/pull/532) +- [feature:add @ConditionalOnConfigReflectEnabled annotation](https://github.com/Tencent/spring-cloud-tencent/pull/538) - [fix:set error handler named EnhancedRestTemplateReporter for RestTemplate](https://github.com/Tencent/spring-cloud-tencent/pull/544) diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java index 980b985d8..a040eb042 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java @@ -18,11 +18,13 @@ package com.tencent.cloud.polaris.config; -import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceAutoRefresher; +import com.tencent.cloud.polaris.config.adapter.AffectedConfigurationPropertiesRebinder; +import com.tencent.cloud.polaris.config.adapter.PolarisConfigPropertyRefresher; import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; -import com.tencent.cloud.polaris.config.adapter.SmartConfigurationPropertiesRebinder; +import com.tencent.cloud.polaris.config.adapter.PolarisRefreshAffectedContextRefresher; +import com.tencent.cloud.polaris.config.adapter.PolarisRefreshEntireContextRefresher; import com.tencent.cloud.polaris.config.annotation.PolarisConfigAnnotationProcessor; -import com.tencent.cloud.polaris.config.condition.ConditionalOnNonDefaultBehavior; +import com.tencent.cloud.polaris.config.condition.ConditionalOnReflectRefreshType; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; import com.tencent.cloud.polaris.config.listener.PolarisConfigChangeEventListener; import com.tencent.cloud.polaris.config.spring.annotation.SpringValueProcessor; @@ -30,6 +32,7 @@ import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.SearchStrategy; @@ -49,17 +52,6 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnProperty(value = "spring.cloud.polaris.config.enabled", matchIfMissing = true) public class PolarisConfigAutoConfiguration { - @Bean - public PolarisPropertySourceAutoRefresher polarisPropertySourceAutoRefresher( - PolarisConfigProperties polarisConfigProperties, - PolarisPropertySourceManager polarisPropertySourceManager, - SpringValueRegistry springValueRegistry, - PlaceholderHelper placeholderHelper, - ContextRefresher contextRefresher) { - return new PolarisPropertySourceAutoRefresher(polarisConfigProperties, - polarisPropertySourceManager, springValueRegistry, placeholderHelper, contextRefresher); - } - @Bean public PolarisConfigAnnotationProcessor polarisConfigAnnotationProcessor() { return new PolarisConfigAnnotationProcessor(); @@ -71,28 +63,46 @@ public class PolarisConfigAutoConfiguration { } @Bean - public SpringValueRegistry springValueRegistry() { - return new SpringValueRegistry(); + @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) + @ConditionalOnReflectRefreshType + public ConfigurationPropertiesRebinder affectedConfigurationPropertiesRebinder( + ConfigurationPropertiesBeans beans) { + return new AffectedConfigurationPropertiesRebinder(beans); } @Bean - public PlaceholderHelper placeholderHelper() { - return new PlaceholderHelper(); + @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) + public PolarisConfigPropertyRefresher polarisRefreshContextPropertySourceAutoRefresher(PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager, ContextRefresher contextRefresher) { + return new PolarisRefreshEntireContextRefresher(polarisConfigProperties, polarisPropertySourceManager, contextRefresher); } - @Bean - public SpringValueProcessor springValueProcessor(PlaceholderHelper placeholderHelper, SpringValueRegistry springValueRegistry, PolarisConfigProperties polarisConfigProperties) { - return new SpringValueProcessor(placeholderHelper, springValueRegistry, polarisConfigProperties); - } + @Configuration(proxyBeanMethods = false) + @ConditionalOnReflectRefreshType + @AutoConfigureBefore(PolarisConfigAutoConfiguration.class) + public static class PolarisReflectRefresherAutoConfiguration { + @Bean + public SpringValueRegistry springValueRegistry() { + return new SpringValueRegistry(); + } - @Bean - @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) - @ConditionalOnNonDefaultBehavior - public ConfigurationPropertiesRebinder smartConfigurationPropertiesRebinder( - ConfigurationPropertiesBeans beans) { - // If using default behavior, not use SmartConfigurationPropertiesRebinder. - // Minimize te possibility of making mistakes. - return new SmartConfigurationPropertiesRebinder(beans); - } + @Bean + public PlaceholderHelper placeholderHelper() { + return new PlaceholderHelper(); + } + + @Bean + public SpringValueProcessor springValueProcessor(PlaceholderHelper placeholderHelper, + SpringValueRegistry springValueRegistry, PolarisConfigProperties polarisConfigProperties) { + return new SpringValueProcessor(placeholderHelper, springValueRegistry, polarisConfigProperties); + } + @Bean + public PolarisConfigPropertyRefresher polarisReflectPropertySourceAutoRefresher(PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager, SpringValueRegistry springValueRegistry, + PlaceholderHelper placeholderHelper) { + return new PolarisRefreshAffectedContextRefresher(polarisConfigProperties, polarisPropertySourceManager, + springValueRegistry, placeholderHelper); + } + } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java index 7c7afdf87..cd186cad4 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java @@ -17,10 +17,10 @@ */ package com.tencent.cloud.polaris.config; +import com.tencent.cloud.polaris.config.adapter.AffectedConfigurationPropertiesRebinder; import com.tencent.cloud.polaris.config.adapter.PolarisConfigFileLocator; import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; -import com.tencent.cloud.polaris.config.adapter.SmartConfigurationPropertiesRebinder; -import com.tencent.cloud.polaris.config.condition.ConditionalOnNonDefaultBehavior; +import com.tencent.cloud.polaris.config.condition.ConditionalOnReflectRefreshType; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; @@ -88,11 +88,9 @@ public class PolarisConfigBootstrapAutoConfiguration { @Bean @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) - @ConditionalOnNonDefaultBehavior - public ConfigurationPropertiesRebinder smartConfigurationPropertiesRebinder( + @ConditionalOnReflectRefreshType + public ConfigurationPropertiesRebinder affectedConfigurationPropertiesRebinder( ConfigurationPropertiesBeans beans) { - // If using default behavior, not use SmartConfigurationPropertiesRebinder. - // Minimize te possibility of making mistakes. - return new SmartConfigurationPropertiesRebinder(beans); + return new AffectedConfigurationPropertiesRebinder(beans); } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/AffectedConfigurationPropertiesRebinder.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/AffectedConfigurationPropertiesRebinder.java new file mode 100644 index 000000000..c18c7f29e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/AffectedConfigurationPropertiesRebinder.java @@ -0,0 +1,82 @@ +/* + * 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.adapter; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.springframework.beans.BeansException; +import org.springframework.boot.context.properties.ConfigurationPropertiesBean; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.cloud.context.properties.ConfigurationPropertiesBeans; +import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder; +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.util.CollectionUtils; + +/** + * Optimize {@link ConfigurationPropertiesRebinder}, only rebuild affected beans. + * + * @author weihubeats + */ +public class AffectedConfigurationPropertiesRebinder extends ConfigurationPropertiesRebinder { + + private ApplicationContext applicationContext; + private Map propertiesBeans = new HashMap<>(); + + public AffectedConfigurationPropertiesRebinder(ConfigurationPropertiesBeans beans) { + super(beans); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + super.setApplicationContext(applicationContext); + + this.applicationContext = applicationContext; + + propertiesBeans = ConfigurationPropertiesBean.getAll(applicationContext); + } + + @Override + public void onApplicationEvent(EnvironmentChangeEvent event) { + if (this.applicationContext.equals(event.getSource())) { + rebindAffectedBeans(event); + } + } + + private void rebindAffectedBeans(EnvironmentChangeEvent event) { + Set changedKeys = event.getKeys(); + + if (CollectionUtils.isEmpty(changedKeys)) { + return; + } + + propertiesBeans.forEach((name, bean) -> { + changedKeys.forEach(key -> { + String propertiesPrefix = Objects.requireNonNull(AnnotationUtils.getValue(bean.getAnnotation())) + .toString(); + if (key.startsWith(propertiesPrefix)) { + rebind(name); + } + }); + }); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java new file mode 100644 index 000000000..2dea15ba4 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyAutoRefresher.java @@ -0,0 +1,116 @@ +/* + * 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.adapter; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener; +import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.lang.NonNull; +import org.springframework.util.CollectionUtils; + +/** + * 1. Listen to the Polaris server configuration publishing event 2. Write the changed + * configuration content to propertySource 3. Refresh the context through contextRefresher + * + * @author lepdou + */ +public abstract class PolarisConfigPropertyAutoRefresher + implements ApplicationListener, PolarisConfigPropertyRefresher { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfigPropertyAutoRefresher.class); + + private final PolarisConfigProperties polarisConfigProperties; + + private final PolarisPropertySourceManager polarisPropertySourceManager; + + private final AtomicBoolean registered = new AtomicBoolean(false); + + public PolarisConfigPropertyAutoRefresher( + PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager) { + this.polarisConfigProperties = polarisConfigProperties; + this.polarisPropertySourceManager = polarisPropertySourceManager; + } + + @Override + public void onApplicationEvent(@NonNull ApplicationReadyEvent event) { + registerPolarisConfigPublishEvent(); + } + + private void registerPolarisConfigPublishEvent() { + if (!polarisConfigProperties.isAutoRefresh()) { + return; + } + + List polarisPropertySources = polarisPropertySourceManager.getAllPropertySources(); + if (CollectionUtils.isEmpty(polarisPropertySources)) { + return; + } + + if (!registered.compareAndSet(false, true)) { + return; + } + + // register polaris config publish event + for (PolarisPropertySource polarisPropertySource : polarisPropertySources) { + polarisPropertySource.getConfigKVFile() + .addChangeListener((ConfigKVFileChangeListener) configKVFileChangeEvent -> { + + LOGGER.info( + "[SCT Config] received polaris config change event and will refresh spring context." + + " namespace = {}, group = {}, fileName = {}", + polarisPropertySource.getNamespace(), + polarisPropertySource.getGroup(), + polarisPropertySource.getFileName()); + + Map source = polarisPropertySource.getSource(); + + for (String changedKey : configKVFileChangeEvent.changedKeys()) { + ConfigPropertyChangeInfo configPropertyChangeInfo = configKVFileChangeEvent + .getChangeInfo(changedKey); + + LOGGER.info("[SCT Config] changed property = {}", configPropertyChangeInfo); + + switch (configPropertyChangeInfo.getChangeType()) { + case MODIFIED: + case ADDED: + source.put(changedKey, configPropertyChangeInfo.getNewValue()); + break; + case DELETED: + source.remove(changedKey); + break; + } + // update the attribute with @Value annotation + refreshSpringValue(changedKey); + } + // update @ConfigurationProperties beans + refreshConfigurationProperties(configKVFileChangeEvent.changedKeys()); + }); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyRefresher.java new file mode 100644 index 000000000..39351e7b4 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigPropertyRefresher.java @@ -0,0 +1,44 @@ +/* + * 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.adapter; + +import java.util.Set; + +/** + * PolarisConfigPropertyRefresher refresh spring value filed and configurationProperties bean + * when config exchange. + * + * @author lingxiao.wlx + */ +public interface PolarisConfigPropertyRefresher { + + /** + * refresh the attribute with @Value annotation. + * + * @param changedKey changedKey + */ + void refreshSpringValue(String changedKey); + + /** + * refresh @ConfigurationProperties beans. + * + * @param changeKeys changeKeys + */ + void refreshConfigurationProperties(Set changeKeys); +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySourceAutoRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySourceAutoRefresher.java deleted file mode 100644 index cd24e2c31..000000000 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertySourceAutoRefresher.java +++ /dev/null @@ -1,217 +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.adapter; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - -import com.tencent.cloud.common.util.JacksonUtils; -import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; -import com.tencent.cloud.polaris.config.enums.RefreshType; -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.SpringValueRegistry; -import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener; -import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; -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.config.ConfigurableBeanFactory; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.cloud.context.environment.EnvironmentChangeEvent; -import org.springframework.cloud.context.refresh.ContextRefresher; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationListener; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.lang.NonNull; -import org.springframework.util.CollectionUtils; - -/** - * 1. Listen to the Polaris server configuration publishing event 2. Write the changed - * configuration content to propertySource 3. Refresh the context through contextRefresher - * - * @author lepdou 2022-03-28 - */ -public class PolarisPropertySourceAutoRefresher - implements ApplicationListener, ApplicationContextAware, BeanFactoryAware { - - private static final Logger LOGGER = LoggerFactory.getLogger(PolarisPropertySourceAutoRefresher.class); - - private final PolarisConfigProperties polarisConfigProperties; - - private final PolarisPropertySourceManager polarisPropertySourceManager; - private final ContextRefresher contextRefresher; - private final AtomicBoolean registered = new AtomicBoolean(false); - private final SpringValueRegistry springValueRegistry; - private final PlaceholderHelper placeholderHelper; - private ConfigurableApplicationContext context; - private TypeConverter typeConverter; - private ConfigurableBeanFactory beanFactory; - - - public PolarisPropertySourceAutoRefresher( - PolarisConfigProperties polarisConfigProperties, - PolarisPropertySourceManager polarisPropertySourceManager, - SpringValueRegistry springValueRegistry, - PlaceholderHelper placeholderHelper, - ContextRefresher contextRefresher) { - this.polarisConfigProperties = polarisConfigProperties; - this.polarisPropertySourceManager = polarisPropertySourceManager; - this.springValueRegistry = springValueRegistry; - this.placeholderHelper = placeholderHelper; - this.contextRefresher = contextRefresher; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.context = (ConfigurableApplicationContext) applicationContext; - this.beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); - this.typeConverter = this.beanFactory.getTypeConverter(); - } - - @Override - public void onApplicationEvent(@NonNull ApplicationReadyEvent event) { - registerPolarisConfigPublishEvent(); - } - - private void registerPolarisConfigPublishEvent() { - if (!polarisConfigProperties.isAutoRefresh()) { - return; - } - - List polarisPropertySources = polarisPropertySourceManager.getAllPropertySources(); - if (CollectionUtils.isEmpty(polarisPropertySources)) { - return; - } - - if (!registered.compareAndSet(false, true)) { - return; - } - - // register polaris config publish event - for (PolarisPropertySource polarisPropertySource : polarisPropertySources) { - polarisPropertySource.getConfigKVFile() - .addChangeListener((ConfigKVFileChangeListener) configKVFileChangeEvent -> { - - LOGGER.info( - "[SCT Config] received polaris config change event and will refresh spring context." - + " namespace = {}, group = {}, fileName = {}", - polarisPropertySource.getNamespace(), - polarisPropertySource.getGroup(), - polarisPropertySource.getFileName()); - - Map source = polarisPropertySource.getSource(); - - for (String changedKey : configKVFileChangeEvent.changedKeys()) { - ConfigPropertyChangeInfo configPropertyChangeInfo = configKVFileChangeEvent - .getChangeInfo(changedKey); - - LOGGER.info("[SCT Config] changed property = {}", configPropertyChangeInfo); - - switch (configPropertyChangeInfo.getChangeType()) { - case MODIFIED: - case ADDED: - source.put(changedKey, configPropertyChangeInfo.getNewValue()); - break; - case DELETED: - source.remove(changedKey); - break; - } - - if (polarisConfigProperties.getRefreshType() == RefreshType.REFLECT) { - Collection targetValues = springValueRegistry.get(beanFactory, changedKey); - if (targetValues == null || targetValues.isEmpty()) { - continue; - } - // update the attribute with @Value annotation - for (SpringValue val : targetValues) { - updateSpringValue(val); - } - } - } - - if (polarisConfigProperties.getRefreshType() == RefreshType.REFLECT) { - // update @ConfigurationProperties beans - context.publishEvent(new EnvironmentChangeEvent(context, configKVFileChangeEvent.changedKeys())); - } - else { - contextRefresher.refresh(); - } - }); - } - } - - private void updateSpringValue(SpringValue springValue) { - try { - Object value = resolvePropertyValue(springValue); - springValue.update(value); - - LOGGER.info("Auto update polaris changed value successfully, new value: {}, {}", value, - springValue); - } - catch (Throwable ex) { - LOGGER.error("Auto update polaris changed value failed, {}", springValue.toString(), ex); - } - } - - - /** - * Logic transplanted from DefaultListableBeanFactory. - * - * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, - * java.lang.String, java.util.Set, org.springframework.beans.TypeConverter) - */ - private Object resolvePropertyValue(SpringValue springValue) { - // value will never be null - Object value = placeholderHelper - .resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder()); - - if (springValue.isJson()) { - value = parseJsonValue((String) value, springValue.getTargetType()); - } - else { - value = springValue.isField() ? this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getField()) : - this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), - springValue.getMethodParameter()); - } - return value; - } - - private Object parseJsonValue(String json, Class targetType) { - try { - return JacksonUtils.json2JavaBean(json, targetType); - } - catch (Throwable ex) { - LOGGER.error("Parsing json '{}' to type {} failed!", json, targetType, ex); - throw ex; - } - } - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = (ConfigurableBeanFactory) beanFactory; - } -} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshAffectedContextRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshAffectedContextRefresher.java new file mode 100644 index 000000000..dffb23adf --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshAffectedContextRefresher.java @@ -0,0 +1,123 @@ +/* + * 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.adapter; + +import java.util.Collection; +import java.util.Set; + +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.SpringValueRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.BeansException; +import org.springframework.beans.TypeConverter; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.lang.NonNull; + +/** + * 1. The refresh of @Value properties is implemented through reflection. + * 2. Implement @ConfigurationProperties bean property refresh via EnvironmentChangeEvent, + * while rebuilding only beans with property changes. + * + * @author lingxiao.wlx + */ +public class PolarisRefreshAffectedContextRefresher extends PolarisConfigPropertyAutoRefresher implements ApplicationContextAware { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisRefreshAffectedContextRefresher.class); + + private final SpringValueRegistry springValueRegistry; + + private final PlaceholderHelper placeholderHelper; + + private ConfigurableApplicationContext context; + + private ConfigurableBeanFactory beanFactory; + + private TypeConverter typeConverter; + + public PolarisRefreshAffectedContextRefresher(PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager, SpringValueRegistry springValueRegistry, + PlaceholderHelper placeholderHelper) { + super(polarisConfigProperties, polarisPropertySourceManager); + this.springValueRegistry = springValueRegistry; + this.placeholderHelper = placeholderHelper; + } + + @Override + public void refreshSpringValue(String changedKey) { + Collection targetValues = springValueRegistry.get(beanFactory, changedKey); + if (targetValues == null || targetValues.isEmpty()) { + return; + } + // update the attribute with @Value annotation + for (SpringValue val : targetValues) { + updateSpringValue(val); + } + } + + @Override + public void refreshConfigurationProperties(Set changeKeys) { + context.publishEvent(new EnvironmentChangeEvent(context, changeKeys)); + } + + private void updateSpringValue(SpringValue springValue) { + try { + Object value = resolvePropertyValue(springValue); + springValue.update(value); + + LOGGER.info("[SCT Config] Auto update polaris changed value successfully, new value: {}, {}", value, + springValue); + } + catch (Throwable ex) { + LOGGER.error("[SCT Config] Auto update polaris changed value failed, {}", springValue.toString(), ex); + } + } + + /** + * Logic transplanted from DefaultListableBeanFactory. + * + * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, + * java.lang.String, java.util.Set, org.springframework.beans.TypeConverter) + */ + private Object resolvePropertyValue(SpringValue springValue) { + // value will never be null + Object value = placeholderHelper + .resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder()); + + value = springValue.isField() ? this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), springValue.getField()) : + this.typeConverter.convertIfNecessary(value, springValue.getTargetType(), + springValue.getMethodParameter()); + + return value; + } + + @Override + public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException { + this.context = (ConfigurableApplicationContext) applicationContext; + this.beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); + this.typeConverter = this.beanFactory.getTypeConverter(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresher.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresher.java new file mode 100644 index 000000000..2f02ab946 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisRefreshEntireContextRefresher.java @@ -0,0 +1,53 @@ +/* + * 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.adapter; + +import java.util.Set; + +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; + +import org.springframework.cloud.context.refresh.ContextRefresher; + +/** + * The default implement of Spring Cloud refreshes the entire Spring Context. + * The disadvantage is that the entire context is rebuilt, which has a large impact and low performance. + * + * @author lingxiao.wlx + */ +public class PolarisRefreshEntireContextRefresher extends PolarisConfigPropertyAutoRefresher { + + private final ContextRefresher contextRefresher; + + public PolarisRefreshEntireContextRefresher(PolarisConfigProperties polarisConfigProperties, + PolarisPropertySourceManager polarisPropertySourceManager, + ContextRefresher contextRefresher) { + super(polarisConfigProperties, polarisPropertySourceManager); + this.contextRefresher = contextRefresher; + } + + @Override + public void refreshSpringValue(String changedKey) { + // do nothing,all config will be refreshed by contextRefresher.refresh + } + + @Override + public void refreshConfigurationProperties(Set changeKeys) { + contextRefresher.refresh(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/SmartConfigurationPropertiesRebinder.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/SmartConfigurationPropertiesRebinder.java deleted file mode 100644 index 93e73616f..000000000 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/SmartConfigurationPropertiesRebinder.java +++ /dev/null @@ -1,122 +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.adapter; - -import java.lang.reflect.Field; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import com.tencent.cloud.polaris.config.enums.RefreshBehavior; - -import org.springframework.beans.BeansException; -import org.springframework.boot.context.properties.ConfigurationPropertiesBean; -import org.springframework.cloud.context.environment.EnvironmentChangeEvent; -import org.springframework.cloud.context.properties.ConfigurationPropertiesBeans; -import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder; -import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.util.ReflectionUtils; - -import static com.tencent.cloud.polaris.config.condition.NonDefaultBehaviorCondition.POLARIS_CONFIG_REFRESH_BEHAVIOR; -import static com.tencent.cloud.polaris.config.enums.RefreshBehavior.ALL_BEANS; - -/** - * Extend {@link ConfigurationPropertiesRebinder}. - *

- * Spring team doesn't seem to support single {@link ConfigurationPropertiesBean} refresh. - *

- * SmartConfigurationPropertiesRebinder can refresh specific - * {@link ConfigurationPropertiesBean} base on the change keys. - *

- * NOTE: We still use Spring's default behavior (full refresh) as default - * behavior, This feature can be considered an advanced feature, it may not be as stable - * as the default behavior. - * - * SmartConfigurationPropertiesRebinder - * - * @author weihubeats 2022-7-10 - */ -public class SmartConfigurationPropertiesRebinder extends ConfigurationPropertiesRebinder { - - private static final String BEANS = "beans"; - - private Map beanMap; - - private ApplicationContext applicationContext; - - private RefreshBehavior refreshBehavior; - - public SmartConfigurationPropertiesRebinder(ConfigurationPropertiesBeans beans) { - super(beans); - fillBeanMap(beans); - } - - @SuppressWarnings("unchecked") - private void fillBeanMap(ConfigurationPropertiesBeans beans) { - this.beanMap = new HashMap<>(); - Field field = ReflectionUtils.findField(beans.getClass(), BEANS); - if (field != null) { - field.setAccessible(true); - this.beanMap.putAll((Map) Optional - .ofNullable(ReflectionUtils.getField(field, beans)) - .orElse(Collections.emptyMap())); - } - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - super.setApplicationContext(applicationContext); - this.applicationContext = applicationContext; - this.refreshBehavior = this.applicationContext.getEnvironment().getProperty( - POLARIS_CONFIG_REFRESH_BEHAVIOR, RefreshBehavior.class, - ALL_BEANS); - } - - @Override - public void onApplicationEvent(EnvironmentChangeEvent event) { - if (this.applicationContext.equals(event.getSource()) - // Backwards compatible - || event.getKeys().equals(event.getSource())) { - switch (refreshBehavior) { - case SPECIFIC_BEAN: - rebindSpecificBean(event); - break; - default: - rebind(); - break; - } - } - } - - private void rebindSpecificBean(EnvironmentChangeEvent event) { - Set refreshedSet = new HashSet<>(); - beanMap.forEach((name, bean) -> event.getKeys().forEach(changeKey -> { - String prefix = AnnotationUtils.getValue(bean.getAnnotation()).toString(); - // prevent multiple refresh one ConfigurationPropertiesBean. - if (changeKey.startsWith(prefix) && refreshedSet.add(name)) { - rebind(name); - } - })); - } -} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnNonDefaultBehavior.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnReflectRefreshType.java similarity index 92% rename from spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnNonDefaultBehavior.java rename to spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnReflectRefreshType.java index d799ec28b..a85687469 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnNonDefaultBehavior.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ConditionalOnReflectRefreshType.java @@ -34,7 +34,7 @@ import org.springframework.context.annotation.Conditional; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented -@Conditional(NonDefaultBehaviorCondition.class) -public @interface ConditionalOnNonDefaultBehavior { +@Conditional(ReflectRefreshTypeCondition.class) +public @interface ConditionalOnReflectRefreshType { } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/NonDefaultBehaviorCondition.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ReflectRefreshTypeCondition.java similarity index 65% rename from spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/NonDefaultBehaviorCondition.java rename to spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ReflectRefreshTypeCondition.java index 529ec1ed8..9db7d5b49 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/NonDefaultBehaviorCondition.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/condition/ReflectRefreshTypeCondition.java @@ -18,7 +18,7 @@ package com.tencent.cloud.polaris.config.condition; -import com.tencent.cloud.polaris.config.enums.RefreshBehavior; +import com.tencent.cloud.polaris.config.enums.RefreshType; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; @@ -26,31 +26,28 @@ import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; /** - * Extend SpringBootCondition. + * The refresh type of reflect condition. * * @author weihubeats 2022-7-13 */ -public class NonDefaultBehaviorCondition extends SpringBootCondition { +public class ReflectRefreshTypeCondition extends SpringBootCondition { /** - * refresh behavior config. + * the property key of refresh type. */ - public static final String POLARIS_CONFIG_REFRESH_BEHAVIOR = "spring.cloud.polaris.config.refresh-behavior"; + public static final String POLARIS_CONFIG_REFRESH_TYPE = "spring.cloud.polaris.config.refresh-type"; - /** - * refresh behavior config default value. - */ - private static final RefreshBehavior DEFAULT_REFRESH_BEHAVIOR = RefreshBehavior.ALL_BEANS; + private static final RefreshType DEFAULT_REFRESH_TYPE = RefreshType.REFRESH_CONTEXT; @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - RefreshBehavior behavior = context.getEnvironment().getProperty( - POLARIS_CONFIG_REFRESH_BEHAVIOR, RefreshBehavior.class, - DEFAULT_REFRESH_BEHAVIOR); - if (DEFAULT_REFRESH_BEHAVIOR == behavior) { + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + RefreshType refreshType = context.getEnvironment() + .getProperty(POLARIS_CONFIG_REFRESH_TYPE, RefreshType.class, DEFAULT_REFRESH_TYPE); + + if (refreshType == DEFAULT_REFRESH_TYPE) { return ConditionOutcome.noMatch("no matched"); } + return ConditionOutcome.match("matched"); } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/PolarisProcessor.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/PolarisProcessor.java index 3058e5eb8..6ff8d8ed0 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/PolarisProcessor.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/PolarisProcessor.java @@ -9,6 +9,7 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; +import org.springframework.lang.NonNull; import org.springframework.util.ReflectionUtils; /** @@ -19,9 +20,9 @@ import org.springframework.util.ReflectionUtils; public abstract class PolarisProcessor implements BeanPostProcessor, PriorityOrdered { @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) + public Object postProcessBeforeInitialization(Object bean, @NonNull String beanName) throws BeansException { - Class clazz = bean.getClass(); + Class clazz = bean.getClass(); for (Field field : findAllField(clazz)) { processField(bean, beanName, field); } @@ -32,7 +33,7 @@ public abstract class PolarisProcessor implements BeanPostProcessor, PriorityOrd } @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException { return bean; } @@ -59,15 +60,15 @@ public abstract class PolarisProcessor implements BeanPostProcessor, PriorityOrd return Ordered.LOWEST_PRECEDENCE; } - private List findAllField(Class clazz) { + private List findAllField(Class clazz) { final List res = new LinkedList<>(); - ReflectionUtils.doWithFields(clazz, field -> res.add(field)); + ReflectionUtils.doWithFields(clazz, res::add); return res; } - private List findAllMethod(Class clazz) { + private List findAllMethod(Class clazz) { final List res = new LinkedList<>(); - ReflectionUtils.doWithMethods(clazz, method -> res.add(method)); + ReflectionUtils.doWithMethods(clazz, res::add); return res; } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessor.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessor.java index d7d09912b..2532f2b9c 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessor.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessor.java @@ -27,6 +27,7 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.Bean; +import org.springframework.lang.NonNull; /** * Spring value processor of field or method which has @Value and xml config placeholders. @@ -68,7 +69,7 @@ public class SpringValueProcessor extends PolarisProcessor implements BeanFactor } @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) + public Object postProcessBeforeInitialization(Object bean, @NonNull String beanName) throws BeansException { if (polarisConfigProperties.isAutoRefresh()) { super.postProcessBeforeInitialization(bean, beanName); diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/PlaceholderHelper.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/PlaceholderHelper.java index 2d7515661..a2223dc78 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/PlaceholderHelper.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/PlaceholderHelper.java @@ -71,7 +71,7 @@ public class PlaceholderHelper { if (beanFactory.getBeanExpressionResolver() == null) { return value; } - Scope scope = (beanDefinition != null ? beanFactory + Scope scope = (beanDefinition != null && beanDefinition.getScope() != null ? beanFactory .getRegisteredScope(beanDefinition.getScope()) : null); return beanFactory.getBeanExpressionResolver() .evaluate(value, new BeanExpressionContext(beanFactory, scope)); @@ -92,7 +92,7 @@ public class PlaceholderHelper { public Set extractPlaceholderKeys(String propertyString) { Set placeholderKeys = Sets.newHashSet(); - if (Strings.isNullOrEmpty(propertyString) || (!isNormalizedPlaceholder(propertyString) && !isExpressionWithPlaceholder(propertyString))) { + if (!isPlaceholder(propertyString)) { return placeholderKeys; } @@ -147,6 +147,11 @@ public class PlaceholderHelper { return placeholderKeys; } + private boolean isPlaceholder(String propertyString) { + return !Strings.isNullOrEmpty(propertyString) && + (isNormalizedPlaceholder(propertyString) || isExpressionWithPlaceholder(propertyString)); + } + private boolean isNormalizedPlaceholder(String propertyString) { return propertyString.startsWith(PLACEHOLDER_PREFIX) && propertyString.contains(PLACEHOLDER_SUFFIX); } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValue.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValue.java index 1f0396112..ea602a925 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValue.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValue.java @@ -97,7 +97,7 @@ public class SpringValue { private void injectMethod(Object newVal) throws InvocationTargetException, IllegalAccessException { Object bean = beanRef.get(); - if (bean == null) { + if (bean == null || methodParameter.getMethod() == null) { return; } methodParameter.getMethod().invoke(bean, newVal); @@ -150,7 +150,12 @@ public class SpringValue { .format("key: %s, beanName: %s, field: %s.%s", key, beanName, bean.getClass() .getName(), field.getName()); } - return String.format("key: %s, beanName: %s, method: %s.%s", key, beanName, bean.getClass().getName(), - methodParameter.getMethod().getName()); + if (null != methodParameter.getMethod()) { + return String.format("key: %s, beanName: %s, method: %s.%s", key, beanName, bean.getClass().getName(), + methodParameter.getMethod().getName()); + } + else { + return String.format("key: %s, beanName: %s", key, beanName); + } } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 666e392aa..8889e99f8 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -42,6 +42,18 @@ "description": "Whether to connect to a remote server, suitable for local development mode.", "sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties" }, + { + "name": "spring.cloud.polaris.config.import-check.enabled", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "Whether to enable import-check." + }, + { + "name": "spring.cloud.polaris.config.preference", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "Whether to preferentially load the remote configuration." + }, { "name": "spring.cloud.polaris.config.refresh-behavior", "type": "com.tencent.cloud.polaris.config.enums.RefreshBehavior", diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java index 03945edc1..6df40aee6 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java @@ -26,7 +26,6 @@ import java.util.Map; import com.google.common.collect.Lists; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; -import com.tencent.cloud.polaris.config.enums.RefreshType; 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.SpringValueRegistry; @@ -41,7 +40,6 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.context.ConfigurableApplicationContext; import static org.mockito.ArgumentMatchers.any; @@ -49,7 +47,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** - * test for {@link PolarisPropertySourceAutoRefresher}. + * test for {@link PolarisRefreshAffectedContextRefresher}. * * @author lepdou 2022-06-11 */ @@ -63,8 +61,6 @@ public class PolarisPropertiesSourceAutoRefresherTest { private PolarisConfigProperties polarisConfigProperties; @Mock private PolarisPropertySourceManager polarisPropertySourceManager; - @Mock - private ContextRefresher contextRefresher; @Mock private SpringValueRegistry springValueRegistry; @@ -74,10 +70,8 @@ public class PolarisPropertiesSourceAutoRefresherTest { @Test public void testConfigFileChanged() throws Exception { - PolarisPropertySourceAutoRefresher refresher = new PolarisPropertySourceAutoRefresher(polarisConfigProperties, - polarisPropertySourceManager, springValueRegistry, placeholderHelper, contextRefresher); - - when(polarisConfigProperties.getRefreshType()).thenReturn(RefreshType.REFLECT); + PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher(polarisConfigProperties, + polarisPropertySourceManager, springValueRegistry, placeholderHelper); ConfigurableApplicationContext applicationContext = mock(ConfigurableApplicationContext.class); ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class); TypeConverter typeConverter = mock(TypeConverter.class); diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/condition/ConditionalOnReflectRefreshTypeTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/condition/ConditionalOnReflectRefreshTypeTest.java new file mode 100644 index 000000000..f2a2d76b4 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/condition/ConditionalOnReflectRefreshTypeTest.java @@ -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.polaris.config.condition; + +import com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration; +import com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration; +import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; +import com.tencent.cloud.polaris.config.adapter.PolarisRefreshAffectedContextRefresher; +import com.tencent.cloud.polaris.config.adapter.PolarisRefreshEntireContextRefresher; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.cloud.polaris.config.enums.RefreshType; +import com.tencent.cloud.polaris.config.spring.annotation.SpringValueProcessor; +import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; +import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.context.refresh.ContextRefresher; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link ConditionalOnReflectRefreshType}. + * + * @author lingxiao.wlx + */ +public class ConditionalOnReflectRefreshTypeTest { + + @Test + public void testReflectEnabled() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(PolarisConfigAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(ConfigurationPropertiesRebinderAutoConfiguration.class)) + .withPropertyValues("spring.application.name=" + "conditionalOnConfigReflectEnabledTest") + .withPropertyValues("server.port=" + 8080) + .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"); + contextRunner.run(context -> { + assertThat(context).hasSingleBean(PlaceholderHelper.class); + assertThat(context).hasSingleBean(SpringValueRegistry.class); + assertThat(context).hasSingleBean(SpringValueProcessor.class); + assertThat(context).hasSingleBean(PolarisRefreshAffectedContextRefresher.class); + }); + } + + @Test + public void testWithoutReflectEnabled() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(PolarisConfigAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(ConfigurationPropertiesRebinderAutoConfiguration.class)) + .withPropertyValues("spring.application.name=" + "conditionalOnConfigReflectEnabledTest") + .withPropertyValues("server.port=" + 8080) + .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") + .withPropertyValues("spring.cloud.polaris.config.enabled=true"); + contextRunner.run(context -> { + assertThat(context).hasSingleBean(PolarisConfigProperties.class); + assertThat(context).hasSingleBean(PolarisPropertySourceManager.class); + assertThat(context).hasSingleBean(ContextRefresher.class); + assertThat(context).hasSingleBean(PolarisRefreshEntireContextRefresher.class); + }); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessorTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessorTest.java new file mode 100644 index 000000000..fb57206bb --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/annotation/SpringValueProcessorTest.java @@ -0,0 +1,153 @@ +/* + * 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.annotation; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Objects; +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.SpringValue; +import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; +import com.tencent.polaris.api.utils.CollectionUtils; +import org.junit.Assert; +import org.junit.Test; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.AutoConfigurations; +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.stereotype.Component; + +/** + * Test for {@link SpringValueProcessor}. + * + * @author lingxiao.wlx + */ +public class SpringValueProcessorTest { + + @Test + public void springValueFiledProcessorTest() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(ValueTest.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("timeout=10000"); + contextRunner.run(context -> { + SpringValueRegistry springValueRegistry = context.getBean(SpringValueRegistry.class); + PolarisConfigAutoConfiguration polarisConfigAutoConfiguration = context.getBean(PolarisConfigAutoConfiguration.class); + BeanFactory beanFactory = polarisConfigAutoConfiguration.beanFactory; + Collection timeout = springValueRegistry.get(beanFactory, "timeout"); + Assert.assertFalse(CollectionUtils.isEmpty(timeout)); + Optional springValueOptional = timeout.stream().findAny(); + Assert.assertTrue(springValueOptional.isPresent()); + + SpringValue springValue = springValueOptional.get(); + Assert.assertEquals("${timeout:1000}", springValue.getPlaceholder()); + Assert.assertTrue(springValue.isField()); + Assert.assertTrue(Objects.nonNull(springValue.getField())); + Assert.assertEquals("timeout", springValue.getField().getName()); + Assert.assertEquals(int.class, springValue.getTargetType()); + + ValueTest bean = context.getBean(ValueTest.class); + Assert.assertEquals(10000, bean.timeout); + }); + } + + @Test + public void springValueMethodProcessorTest() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(PolarisConfigBootstrapAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class)) + .withConfiguration(AutoConfigurations.of(ValueTest.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 -> { + SpringValueRegistry springValueRegistry = context.getBean(SpringValueRegistry.class); + PolarisConfigAutoConfiguration polarisConfigAutoConfiguration = context.getBean(PolarisConfigAutoConfiguration.class); + BeanFactory beanFactory = polarisConfigAutoConfiguration.beanFactory; + Collection name = springValueRegistry.get(beanFactory, "name"); + Assert.assertFalse(CollectionUtils.isEmpty(name)); + Optional springValueOptional = name.stream().findAny(); + Assert.assertTrue(springValueOptional.isPresent()); + + SpringValue springValue = springValueOptional.get(); + Method method = springValue.getMethodParameter().getMethod(); + Assert.assertTrue(Objects.nonNull(method)); + Assert.assertEquals("setName", method.getName()); + Assert.assertEquals("${name:1000}", springValue.getPlaceholder()); + Assert.assertFalse(springValue.isField()); + Assert.assertEquals(String.class, springValue.getTargetType()); + + Assert.assertEquals("test", ValueTest.name); + }); + } + + @Configuration + @EnableAutoConfiguration + static class PolarisConfigAutoConfiguration { + + @Autowired + private BeanFactory beanFactory; + + public BeanFactory getBeanFactory() { + return beanFactory; + } + + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + } + + @Component + private static class ValueTest { + @Value("${timeout:1000}") + private int timeout; + + private static String name; + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + @Value("${name:1000}") + public void setName(String name) { + ValueTest.name = name; + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/Person.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/Person.java new file mode 100644 index 000000000..5a09fb880 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/Person.java @@ -0,0 +1,67 @@ +/* + * 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 org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; + +/** + * Test example. + * + * @author lingxiao.wlx + */ +public class Person implements BeanFactoryAware { + + private String name; + + private String age; + + private BeanFactory beanFactory; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAge() { + return age; + } + + public void setAge(String age) { + this.age = age; + } + + public BeanFactory getBeanFactory() { + return beanFactory; + } + + @Override + public String toString() { + return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/PlaceholderHelperTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/PlaceholderHelperTest.java new file mode 100644 index 000000000..a605dda88 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/PlaceholderHelperTest.java @@ -0,0 +1,88 @@ +/* + * 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.Set; + +import com.tencent.polaris.api.utils.CollectionUtils; +import org.junit.Assert; +import org.junit.Test; + +/** + * Test for {@link PlaceholderHelper}. + * + * @author lingxiao.wlx + */ +public class PlaceholderHelperTest { + + private static final PlaceholderHelper PLACEHOLDER_HELPER = new PlaceholderHelper(); + + @Test + public void extractNormalPlaceholderKeysTest() { + final String placeholderCase = "${some.key}"; + final String placeholderCase1 = "${some.key:${some.other.key:100}}"; + final String placeholderCase2 = "${${some.key}}"; + final String placeholderCase3 = "${${some.key:other.key}}"; + final String placeholderCase4 = "${${some.key}:${another.key}}"; + final String placeholderCase5 = "#{new java.text.SimpleDateFormat('${some.key}').parse('${another.key}')}"; + + Set placeholderKeys = PLACEHOLDER_HELPER.extractPlaceholderKeys(placeholderCase); + Assert.assertEquals(1, placeholderKeys.size()); + Assert.assertTrue(placeholderKeys.contains("some.key")); + + Set placeholderKeys1 = PLACEHOLDER_HELPER.extractPlaceholderKeys(placeholderCase1); + Assert.assertEquals(2, placeholderKeys1.size()); + Assert.assertTrue(placeholderKeys1.contains("some.key")); + Assert.assertTrue(placeholderKeys1.contains("some.other.key")); + + Set placeholderKeys2 = PLACEHOLDER_HELPER.extractPlaceholderKeys(placeholderCase2); + Assert.assertEquals(1, placeholderKeys2.size()); + Assert.assertTrue(placeholderKeys2.contains("some.key")); + + Set placeholderKeys3 = PLACEHOLDER_HELPER.extractPlaceholderKeys(placeholderCase3); + Assert.assertEquals(1, placeholderKeys3.size()); + Assert.assertTrue(placeholderKeys3.contains("some.key")); + + Set placeholderKeys4 = PLACEHOLDER_HELPER.extractPlaceholderKeys(placeholderCase4); + Assert.assertEquals(2, placeholderKeys4.size()); + Assert.assertTrue(placeholderKeys4.contains("some.key")); + Assert.assertTrue(placeholderKeys4.contains("another.key")); + + Set placeholderKeys5 = PLACEHOLDER_HELPER.extractPlaceholderKeys(placeholderCase5); + Assert.assertEquals(2, placeholderKeys5.size()); + Assert.assertTrue(placeholderKeys5.contains("some.key")); + Assert.assertTrue(placeholderKeys5.contains("another.key")); + } + + @Test + public void extractIllegalPlaceholderKeysTest() { + final String placeholderCase = "${some.key"; + final String placeholderCase1 = "{some.key}"; + final String placeholderCase2 = "some.key"; + + Set placeholderKeys = PLACEHOLDER_HELPER.extractPlaceholderKeys(placeholderCase); + Assert.assertTrue(CollectionUtils.isEmpty(placeholderKeys)); + + Set placeholderKeys1 = PLACEHOLDER_HELPER.extractPlaceholderKeys(placeholderCase1); + Assert.assertTrue(CollectionUtils.isEmpty(placeholderKeys1)); + + Set placeholderKeys2 = PLACEHOLDER_HELPER.extractPlaceholderKeys(placeholderCase2); + Assert.assertTrue(CollectionUtils.isEmpty(placeholderKeys2)); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/SpringValueDefinitionProcessorTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/SpringValueDefinitionProcessorTest.java new file mode 100644 index 000000000..c82980a41 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/SpringValueDefinitionProcessorTest.java @@ -0,0 +1,75 @@ +/* + * 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 name = springValueRegistry.get(beanFactory, "name"); + Assert.assertFalse(CollectionUtils.isEmpty(name)); + Optional 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 age = springValueRegistry.get(beanFactory, "age"); + Assert.assertFalse(CollectionUtils.isEmpty(age)); + Optional 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()); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/resources/application-test.properties b/spring-cloud-starter-tencent-polaris-config/src/test/resources/application-test.properties new file mode 100644 index 000000000..613f52d5e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/resources/application-test.properties @@ -0,0 +1,2 @@ +name=test +age=10 diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/resources/bean.xml b/spring-cloud-starter-tencent-polaris-config/src/test/resources/bean.xml new file mode 100644 index 000000000..e60613a5a --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/resources/bean.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-cloud-tencent-examples/polaris-config-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-config-example/src/main/resources/bootstrap.yml index b23370d73..30cdb0669 100644 --- a/spring-cloud-tencent-examples/polaris-config-example/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-config-example/src/main/resources/bootstrap.yml @@ -12,8 +12,7 @@ spring: groups: - name: ${spring.application.name} # group name files: [ "config/application.properties", "config/bootstrap.yml" ] # config/application.properties takes precedence over config/bootstrap.yml - refresh-behavior: SPECIFIC_BEAN - refresh-type: REFLECT + refresh-type: refresh_context management: endpoints: web: