diff --git a/CHANGELOG.md b/CHANGELOG.md index 06c1f8175..368093730 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ # Change Log --- +- [Optimize:optimize SpringValueProcessor.](https://github.com/Tencent/spring-cloud-tencent/pull/668) 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 26635ed44..562fc67f2 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 @@ -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 PROPERTY_VALUES_PROCESSED_BEAN_FACTORIES = Sets.newConcurrentHashSet(); + private static final Map> 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 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 getBeanName2SpringValueDefinitions(BeanDefinitionRegistry registry) { + Multimap 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 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 propertyValues = mutablePropertyValues.getPropertyValueList(); + for (PropertyValue propertyValue : propertyValues) { + Object value = propertyValue.getValue(); + if (!(value instanceof TypedStringValue)) { + continue; + } + String placeholder = ((TypedStringValue) value).getValue(); + Set keys = placeholderHelper.extractPlaceholderKeys(placeholder); + + if (keys.isEmpty()) { + continue; + } + + for (String key : keys) { + springValueDefinitions.put(beanName, new SpringValueDefinition(key, placeholder, propertyValue.getName())); + } + } + } } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValueDefinitionProcessor.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValueDefinitionProcessor.java deleted file mode 100644 index 9064667c0..000000000 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/spring/property/SpringValueDefinitionProcessor.java +++ /dev/null @@ -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. - * - *
- *  <bean class="com.demo.bean.XmlBean">
- *    <property name="timeout" value="${timeout:200}"/>
- *    <property name="batch" value="${batch:100}"/>
- *  </bean>
- * 
- * - * This source file was originally from: - * - * SpringValueDefinitionProcessor - * - * @author weihubeats 2022-7-10 - */ -public class SpringValueDefinitionProcessor implements BeanDefinitionRegistryPostProcessor { - private static final Map> beanName2SpringValueDefinitions = - Maps.newConcurrentMap(); - private static final Set 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 getBeanName2SpringValueDefinitions(BeanDefinitionRegistry registry) { - Multimap 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 springValueDefinitions = beanName2SpringValueDefinitions.get(beanRegistry); - - String[] beanNames = beanRegistry.getBeanDefinitionNames(); - for (String beanName : beanNames) { - BeanDefinition beanDefinition = beanRegistry.getBeanDefinition(beanName); - MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues(); - List propertyValues = mutablePropertyValues.getPropertyValueList(); - for (PropertyValue propertyValue : propertyValues) { - Object value = propertyValue.getValue(); - if (!(value instanceof TypedStringValue)) { - continue; - } - String placeholder = ((TypedStringValue) value).getValue(); - Set keys = placeholderHelper.extractPlaceholderKeys(placeholder); - - if (keys.isEmpty()) { - continue; - } - - for (String key : keys) { - springValueDefinitions.put(beanName, new SpringValueDefinition(key, placeholder, propertyValue.getName())); - } - } - } - } -} 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 index 99f1b9b14..b4bd33afd 100644 --- 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 @@ -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 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()); + }); + + } + @Configuration @EnableAutoConfiguration static class PolarisConfigAutoConfiguration { @@ -173,4 +222,9 @@ public class SpringValueProcessorTest { ValueTest.name = name; } } + + @Configuration + @ImportResource("classpath:bean.xml") + static class XMLBeamDefinitionTest { + } } 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 deleted file mode 100644 index c82980a41..000000000 --- a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/spring/property/SpringValueDefinitionProcessorTest.java +++ /dev/null @@ -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 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/bean.xml b/spring-cloud-starter-tencent-polaris-config/src/test/resources/bean.xml index e60613a5a..e6484c467 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/test/resources/bean.xml +++ b/spring-cloud-starter-tencent-polaris-config/src/test/resources/bean.xml @@ -11,28 +11,4 @@ - - - - - - - - - - - - - - - - - - - - - - - -