Skip to content

Commit 1c7a3c6

Browse files
committed
1 parent 6cd6f75 commit 1c7a3c6

File tree

3 files changed

+123
-5
lines changed

3 files changed

+123
-5
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrar.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@
1616

1717
package org.springframework.boot.context.properties;
1818

19+
import org.springframework.aop.scope.ScopedProxyUtils;
1920
import org.springframework.beans.factory.BeanFactory;
2021
import org.springframework.beans.factory.ListableBeanFactory;
21-
import org.springframework.beans.factory.config.BeanDefinition;
22+
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
23+
import org.springframework.beans.factory.config.BeanDefinitionHolder;
24+
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
2225
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
2326
import org.springframework.beans.factory.support.RootBeanDefinition;
2427
import org.springframework.boot.context.properties.bind.BindMethod;
28+
import org.springframework.context.annotation.AnnotationScopeMetadataResolver;
29+
import org.springframework.context.annotation.ScopeMetadata;
30+
import org.springframework.context.annotation.ScopeMetadataResolver;
31+
import org.springframework.context.annotation.ScopedProxyMode;
2532
import org.springframework.core.annotation.MergedAnnotation;
2633
import org.springframework.core.annotation.MergedAnnotations;
2734
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
@@ -38,6 +45,8 @@
3845
*/
3946
final class ConfigurationPropertiesBeanRegistrar {
4047

48+
private static final ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
49+
4150
private final BeanDefinitionRegistry registry;
4251

4352
private final BeanFactory beanFactory;
@@ -75,17 +84,25 @@ private void registerBeanDefinition(String beanName, Class<?> type,
7584
MergedAnnotation<ConfigurationProperties> annotation) {
7685
Assert.state(annotation.isPresent(), () -> "No " + ConfigurationProperties.class.getSimpleName()
7786
+ " annotation found on '" + type.getName() + "'.");
78-
this.registry.registerBeanDefinition(beanName, createBeanDefinition(beanName, type));
87+
BeanDefinitionReaderUtils.registerBeanDefinition(createBeanDefinition(beanName, type), this.registry);
7988
}
8089

81-
private BeanDefinition createBeanDefinition(String beanName, Class<?> type) {
90+
private BeanDefinitionHolder createBeanDefinition(String beanName, Class<?> type) {
8291
BindMethod bindMethod = ConfigurationPropertiesBean.deduceBindMethod(type);
8392
RootBeanDefinition definition = new RootBeanDefinition(type);
8493
BindMethodAttribute.set(definition, bindMethod);
8594
if (bindMethod == BindMethod.VALUE_OBJECT) {
8695
definition.setInstanceSupplier(() -> ConstructorBound.from(this.beanFactory, beanName, type));
8796
}
88-
return definition;
97+
ScopeMetadata metadata = scopeMetadataResolver.resolveScopeMetadata(new AnnotatedGenericBeanDefinition(type));
98+
definition.setScope(metadata.getScopeName());
99+
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definition, beanName);
100+
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
101+
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
102+
return definitionHolder;
103+
}
104+
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
105+
return ScopedProxyUtils.createScopedProxy(definitionHolder, this.registry, proxyTargetClass);
89106
}
90107

91108
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanRegistrarTests.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@
2020

2121
import org.junit.jupiter.api.Test;
2222

23+
import org.springframework.aop.scope.ScopedProxyFactoryBean;
2324
import org.springframework.beans.factory.config.BeanDefinition;
2425
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
2526
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
2627
import org.springframework.beans.factory.support.GenericBeanDefinition;
2728
import org.springframework.beans.factory.support.RootBeanDefinition;
2829
import org.springframework.boot.context.properties.bind.BindMethod;
30+
import org.springframework.context.annotation.Scope;
31+
import org.springframework.context.annotation.ScopedProxyMode;
2932

3033
import static org.assertj.core.api.Assertions.assertThat;
3134
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@@ -44,12 +47,38 @@ class ConfigurationPropertiesBeanRegistrarTests {
4447
private final ConfigurationPropertiesBeanRegistrar registrar = new ConfigurationPropertiesBeanRegistrar(
4548
this.registry);
4649

50+
@Test
51+
void registerScopedBeanDefinition() {
52+
String beanName = "scopedbeancp-" + ScopedBeanConfigurationProperties.class.getName();
53+
this.registrar.register(ScopedBeanConfigurationProperties.class);
54+
BeanDefinition beanDefinition = this.registry.getBeanDefinition(beanName);
55+
assertThat(beanDefinition).isNotNull();
56+
assertThat(beanDefinition.getBeanClassName()).isEqualTo(ScopedBeanConfigurationProperties.class.getName());
57+
assertThat(beanDefinition.getScope()).isEqualTo(BeanDefinition.SCOPE_PROTOTYPE);
58+
}
59+
60+
@Test
61+
void registerScopedBeanDefinitionWithProxyMode() {
62+
String beanName = "scopedbeancp-" + ProxyScopedBeanConfigurationProperties.class.getName();
63+
this.registrar.register(ProxyScopedBeanConfigurationProperties.class);
64+
BeanDefinition proxiedBeanDefinition = this.registry.getBeanDefinition(beanName);
65+
assertThat(proxiedBeanDefinition).isNotNull();
66+
assertThat(proxiedBeanDefinition.getBeanClassName()).isEqualTo(ScopedProxyFactoryBean.class.getName());
67+
String targetBeanName = (String) proxiedBeanDefinition.getPropertyValues().get("targetBeanName");
68+
assertThat(targetBeanName).isNotNull();
69+
BeanDefinition beanDefinition = this.registry.getBeanDefinition(targetBeanName);
70+
assertThat(beanDefinition).isNotNull();
71+
assertThat(beanDefinition.getBeanClassName()).isEqualTo(ProxyScopedBeanConfigurationProperties.class.getName());
72+
assertThat(beanDefinition.getScope()).isEqualTo(BeanDefinition.SCOPE_PROTOTYPE);
73+
}
74+
4775
@Test
4876
void registerWhenNotAlreadyRegisteredAddBeanDefinition() {
4977
String beanName = "beancp-" + BeanConfigurationProperties.class.getName();
5078
this.registrar.register(BeanConfigurationProperties.class);
5179
BeanDefinition definition = this.registry.getBeanDefinition(beanName);
5280
assertThat(definition).isNotNull();
81+
assertThat(definition.getScope()).isEqualTo(BeanDefinition.SCOPE_SINGLETON);
5382
assertThat(definition.getBeanClassName()).isEqualTo(BeanConfigurationProperties.class.getName());
5483
}
5584

@@ -99,6 +128,18 @@ static class BeanConfigurationProperties {
99128

100129
}
101130

131+
@ConfigurationProperties(prefix = "scopedbeancp")
132+
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
133+
static class ScopedBeanConfigurationProperties {
134+
135+
}
136+
137+
@ConfigurationProperties(prefix = "scopedbeancp")
138+
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = BeanDefinition.SCOPE_PROTOTYPE)
139+
static class ProxyScopedBeanConfigurationProperties {
140+
141+
}
142+
102143
static class NoAnnotationConfigurationProperties {
103144

104145
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.Optional;
3434
import java.util.Properties;
3535
import java.util.Set;
36+
import java.util.UUID;
3637

3738
import jakarta.annotation.PostConstruct;
3839
import jakarta.validation.Valid;
@@ -48,6 +49,7 @@
4849
import org.springframework.beans.factory.ObjectProvider;
4950
import org.springframework.beans.factory.annotation.Autowired;
5051
import org.springframework.beans.factory.annotation.Value;
52+
import org.springframework.beans.factory.config.BeanDefinition;
5153
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
5254
import org.springframework.beans.factory.support.AbstractBeanDefinition;
5355
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -1243,6 +1245,24 @@ void loadWhenBindingToConstructorParametersWithConversionToCustomListImplementat
12431245
"b");
12441246
}
12451247

1248+
@Test
1249+
void loadPrototypeScopedProperties() {
1250+
load(PrototypeScopePropertiesConfiguration.class);
1251+
PrototypeScopeProperties p1 = this.context.getBean(PrototypeScopeProperties.class);
1252+
PrototypeScopeProperties p2 = this.context.getBean(PrototypeScopeProperties.class);
1253+
assertThat(p1.id).isNotNull();
1254+
assertThat(p2.id).isNotNull();
1255+
assertThat(p1.id).isNotEqualTo(p2.id);
1256+
}
1257+
1258+
@Test
1259+
void loadProxyScopedProperties() {
1260+
load(ProxyScopePropertiesConfiguration.class, "name=test");
1261+
ProxyScopeProperties p = this.context.getBean(ProxyScopeProperties.class);
1262+
assertThat(p.name).isEqualTo("test");
1263+
assertThat(p.getName()).isEqualTo("test");
1264+
}
1265+
12461266
@Test
12471267
void loadWhenBindingToJavaBeanWithConversionToCustomListImplementation() {
12481268
load(SetterBoundCustomListPropertiesConfiguration.class, "test.values=a,b");
@@ -1493,12 +1513,52 @@ static PropertySourcesPlaceholderConfigurer configurer2() {
14931513

14941514
}
14951515

1516+
@EnableConfigurationProperties(PrototypeScopeProperties.class)
1517+
@Configuration(proxyBeanMethods = false)
1518+
static class PrototypeScopePropertiesConfiguration {
1519+
1520+
}
1521+
1522+
@ConfigurationProperties
1523+
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
1524+
static class PrototypeScopeProperties {
1525+
1526+
private final String id = UUID.randomUUID().toString();
1527+
1528+
String getId() {
1529+
return this.id;
1530+
}
1531+
1532+
}
1533+
1534+
@EnableConfigurationProperties(ProxyScopeProperties.class)
1535+
@Configuration(proxyBeanMethods = false)
1536+
static class ProxyScopePropertiesConfiguration {
1537+
1538+
}
1539+
1540+
@ConfigurationProperties
1541+
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
1542+
static class ProxyScopeProperties {
1543+
1544+
private String name;
1545+
1546+
String getName() {
1547+
return this.name;
1548+
}
1549+
1550+
void setName(String name) {
1551+
this.name = name;
1552+
}
1553+
1554+
}
1555+
14961556
@Configuration(proxyBeanMethods = false)
14971557
@EnableConfigurationProperties
14981558
static class PrototypePropertiesConfiguration {
14991559

15001560
@Bean
1501-
@Scope("prototype")
1561+
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
15021562
@ConfigurationProperties("example")
15031563
PrototypeBean prototypeBean() {
15041564
return new PrototypeBean();

0 commit comments

Comments
 (0)