Optimize:optimize SpringValueProcessor (#668)

pull/688/head
lingxiao,wu 2 years ago committed by GitHub
parent 625f919cf4
commit 61846a3aaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +1,4 @@
# Change Log # Change Log
--- ---
- [Optimize:optimize SpringValueProcessor.](https://github.com/Tencent/spring-cloud-tencent/pull/668)

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

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

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

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

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

Loading…
Cancel
Save