Skip to content

Commit 838639c

Browse files
committed
Defer bean context init until singletons ready
Split bean assignment from metadata initialization by replacing `initialize(bean)` with `setBean(bean)` and adding a separate `initialize()` phase. Bean context now lazily resolves the target bean from the Spring context when needed, including bean-name deduction and fallback lookup by type. The listener now initializes all bean contexts in `afterSingletonsInstantiated()`, ensuring property metadata is prepared after binding lifecycle setup. Tests were updated for the new API and expanded with nested `test.projects.*` properties to cover deeper binding/event scenarios.
1 parent 8d17e44 commit 838639c

4 files changed

Lines changed: 55 additions & 16 deletions

File tree

microsphere-spring-boot-core/src/main/java/io/microsphere/spring/boot/context/properties/bind/ConfigurationPropertiesBeanContext.java

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import static io.microsphere.constants.SeparatorConstants.LINE_SEPARATOR;
4545
import static io.microsphere.constants.SymbolConstants.DOT;
4646
import static io.microsphere.constants.SymbolConstants.DOT_CHAR;
47+
import static io.microsphere.constants.SymbolConstants.HYPHEN;
4748
import static io.microsphere.lang.function.ThrowableSupplier.execute;
4849
import static io.microsphere.logging.LoggerFactory.getLogger;
4950
import static io.microsphere.reflect.FieldUtils.findAllDeclaredFields;
@@ -61,7 +62,6 @@
6162
import static org.springframework.beans.BeanUtils.getPropertyDescriptors;
6263
import static org.springframework.boot.context.properties.bind.Bindable.of;
6364
import static org.springframework.boot.context.properties.source.ConfigurationPropertyName.of;
64-
import static org.springframework.core.ResolvableType.forInstance;
6565
import static org.springframework.util.ClassUtils.isAssignableValue;
6666
import static org.springframework.util.ClassUtils.isPrimitiveOrWrapper;
6767

@@ -100,6 +100,7 @@ class ConfigurationPropertiesBeanContext {
100100
@Nullable
101101
private volatile Object initializedBean;
102102

103+
@Nullable
103104
private volatile BeanWrapper beanWrapper;
104105

105106
/**
@@ -125,7 +126,7 @@ class ConfigurationPropertiesBeanContext {
125126
this.beanProperties = newHashMap();
126127
}
127128

128-
void initialize(Object bean) {
129+
void setBean(Object bean) {
129130
if (!this.beanType.isInstance(bean)) {
130131
if (logger.isWarnEnabled()) {
131132
Class<?> beanClass = bean.getClass();
@@ -135,9 +136,10 @@ void initialize(Object bean) {
135136
return;
136137
}
137138
this.initializedBean = bean;
138-
this.beanWrapper = new BeanWrapperImpl(bean);
139-
initBeanProperties(bean);
139+
}
140140

141+
void initialize() {
142+
initBeanProperties();
141143
if (logger.isTraceEnabled()) {
142144
StringJoiner beanInfo = new StringJoiner(LINE_SEPARATOR);
143145
for (Map.Entry<ConfigurationPropertyName, ConfigurationPropertiesBeanProperty> entry : this.beanProperties.entrySet()) {
@@ -150,10 +152,10 @@ void initialize(Object bean) {
150152
}
151153
}
152154

153-
private void initBeanProperties(Object bean) {
154-
String prefix = this.prefix;
155-
ConfigurationPropertyName prefixName = of(prefix);
156-
initBeanProperties(forInstance(bean), prefixName, null);
155+
private void initBeanProperties() {
156+
Object bean = getBean();
157+
this.beanWrapper = new BeanWrapperImpl(bean);
158+
initBeanProperties(this.beanType, of(this.prefix), null);
157159
}
158160

159161
private void initBeanProperties(ResolvableType beanType, ConfigurationPropertyName prefixName, String nestedPath) {
@@ -170,6 +172,37 @@ private void initBeanProperties(ResolvableType beanType, ConfigurationPropertyNa
170172
}
171173
}
172174

175+
Object getBean() {
176+
Object bean = this.initializedBean;
177+
if (bean == null) {
178+
// Get the bean from the Spring context by its name
179+
String beanName = getBeanName();
180+
bean = this.context.getBean(beanName, getBeanClass());
181+
this.initializedBean = bean;
182+
}
183+
return bean;
184+
}
185+
186+
String getBeanName() {
187+
Class<?> beanClass = getBeanClass();
188+
ConfigurableApplicationContext context = this.context;
189+
String beanName = deduceBeanName(beanClass);
190+
if (context.containsBean(beanName)) {
191+
return beanName;
192+
} else {
193+
String[] beanNames = context.getBeanNamesForType(beanClass, true, false);
194+
return beanNames[0];
195+
}
196+
}
197+
198+
String deduceBeanName(Class<?> beanClass) {
199+
return this.prefix + HYPHEN + beanClass.getName();
200+
}
201+
202+
Class<?> getBeanClass() {
203+
return this.beanType.getRawClass();
204+
}
205+
173206
private Constructor<?> getBindConstructor(ResolvableType beanType) {
174207
return BindUtils.getBindConstructor(of(beanType), true);
175208
}
@@ -247,7 +280,7 @@ private void initBeanProperty(ResolvableType beanType, Field field, Configuratio
247280
}
248281

249282
Object readFieldValue(Field field, @Nullable String nestedPath) {
250-
Object instance = getInstance(this.initializedBean, nestedPath);
283+
Object instance = getInstance(getBean(), nestedPath);
251284
if (instance == null) {
252285
return null;
253286
}
@@ -395,7 +428,7 @@ void publishEvent(ConfigurationProperty property, ConfigurationPropertiesBeanPro
395428
Object oldValue, Object newValue) {
396429
String propertyName = configurationPropertiesBeanProperty.getName();
397430
ResolvableType propertyType = configurationPropertiesBeanProperty.getType();
398-
this.context.publishEvent(new ConfigurationPropertiesBeanPropertyChangedEvent(initializedBean, propertyName,
431+
this.context.publishEvent(new ConfigurationPropertiesBeanPropertyChangedEvent(getBean(), propertyName,
399432
propertyType, oldValue, newValue, property));
400433
}
401434
}

microsphere-spring-boot-core/src/main/java/io/microsphere/spring/boot/context/properties/bind/EventPublishingConfigurationPropertiesBeanPropertyChangedListener.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ void initConfigurationPropertiesBeanContext(ConfigurationPropertyName name, Bind
112112
name, target, context.getDepth());
113113
}
114114
} else {
115-
configurationPropertiesBeanContext.initialize(bean);
115+
configurationPropertiesBeanContext.setBean(bean);
116116
if (logger.isTraceEnabled()) {
117117
logger.trace("The ConfigurationPropertiesBean binding is finished[name : '{}' , target : {} , depth : {} , bean : '{}']",
118118
name, target, context.getDepth(), bean);
@@ -245,6 +245,9 @@ public void setApplicationContext(ApplicationContext context) throws BeansExcept
245245
*/
246246
@Override
247247
public void afterSingletonsInstantiated() {
248+
for (ConfigurationPropertiesBeanContext beanContext : this.beanContexts.values()) {
249+
beanContext.initialize();
250+
}
248251
bound = true;
249252
}
250253

microsphere-spring-boot-core/src/test/java/io/microsphere/spring/boot/context/properties/bind/ConfigurationPropertiesBeanContextTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@ void setUp() {
6767
}
6868

6969
@Test
70-
void testInitialize() {
70+
void testSetBean() {
7171
ServerProperties serverProperties = new ServerProperties();
72-
this.beanContext.initialize(serverProperties);
72+
this.beanContext.setBean(serverProperties);
7373
assertNull(serverProperties.getPort());
7474

75-
this.beanContext.initialize(new JacksonProperties());
75+
this.beanContext.setBean(new JacksonProperties());
7676
}
7777

7878
@Test
@@ -83,7 +83,7 @@ void testSetProperty() {
8383
ConfigurationProperty property = newConfigurationProperty(propertyName, propertyValue);
8484

8585
ServerProperties serverProperties = new ServerProperties();
86-
this.beanContext.initialize(serverProperties);
86+
this.beanContext.setBean(serverProperties);
8787

8888
this.beanContext.setProperty(property, propertyValue, false);
8989

@@ -96,7 +96,7 @@ void testSetProperty() {
9696
@Test
9797
void testGetPropertyValueOnFailed() {
9898
assertNull(this.beanContext.getPropertyValue("invalid.property.name"));
99-
this.beanContext.initialize(new ServerProperties());
99+
this.beanContext.setBean(new ServerProperties());
100100
assertNull(this.beanContext.getPropertyValue("invalid.property.name"));
101101
}
102102

microsphere-spring-boot-core/src/test/java/io/microsphere/spring/boot/context/properties/bind/EventPublishingConfigurationPropertiesBeanPropertyChangedListenerTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@
9292
"test.aliases[1]=b",
9393
"test.aliases[2]=c",
9494
"test.ports=7070,8080,9090",
95+
"test.projects.project-1[0].build.location=META-INF/build-info.properties",
96+
"test.projects.project-1[1].build.encoding=UTF-8",
97+
"test.projects.project-2[0].git.location=my-git.properties",
9598

9699
// TestConstructorBindingConfigurationProperties
97100
"test.constructor.binding.name=test-constructor-binding-name",

0 commit comments

Comments
 (0)