Skip to content

Commit 88ce11a

Browse files
committed
Merge branch '6.2.x'
2 parents 2e086bd + d890a38 commit 88ce11a

File tree

4 files changed

+108
-74
lines changed

4 files changed

+108
-74
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.context.annotation;
1818

19+
import java.lang.reflect.Constructor;
1920
import java.lang.reflect.Method;
2021
import java.util.ArrayList;
2122
import java.util.Arrays;
@@ -28,6 +29,7 @@
2829
import org.apache.commons.logging.LogFactory;
2930
import org.jspecify.annotations.Nullable;
3031

32+
import org.springframework.beans.BeanUtils;
3133
import org.springframework.beans.factory.BeanDefinitionStoreException;
3234
import org.springframework.beans.factory.BeanRegistrar;
3335
import org.springframework.beans.factory.ListableBeanFactory;
@@ -290,8 +292,8 @@ private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
290292
}
291293

292294
if (logger.isTraceEnabled()) {
293-
logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
294-
configClass.getMetadata().getClassName(), beanName));
295+
logger.trace("Registering bean definition for @Bean method %s.%s()"
296+
.formatted(configClass.getMetadata().getClassName(), beanName));
295297
}
296298
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
297299
}
@@ -351,9 +353,8 @@ protected boolean isOverriddenByExistingDefinition(BeanMethod beanMethod, String
351353
"@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
352354
}
353355
if (logger.isDebugEnabled()) {
354-
logger.debug(String.format("Skipping bean definition for %s: a definition for bean '%s' " +
355-
"already exists. This top-level bean definition is considered as an override.",
356-
beanMethod, beanName));
356+
logger.debug("Skipping bean definition for %s: a definition for bean '%s' already exists. " +
357+
"This top-level bean definition is considered as an override.".formatted(beanMethod, beanName));
357358
}
358359
return true;
359360
}
@@ -379,9 +380,11 @@ private void loadBeanDefinitionsFromImportedResources(
379380
BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
380381
if (reader == null) {
381382
try {
383+
Constructor<? extends BeanDefinitionReader> constructor =
384+
readerClass.getDeclaredConstructor(BeanDefinitionRegistry.class);
382385
// Instantiate the specified BeanDefinitionReader
383-
reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
384-
// Delegate the current ResourceLoader to it if possible
386+
reader = BeanUtils.instantiateClass(constructor, this.registry);
387+
// Delegate the current ResourceLoader and Environment to it if possible
385388
if (reader instanceof AbstractBeanDefinitionReader abdr) {
386389
abdr.setResourceLoader(this.resourceLoader);
387390
abdr.setEnvironment(this.environment);
@@ -390,11 +393,9 @@ private void loadBeanDefinitionsFromImportedResources(
390393
}
391394
catch (Throwable ex) {
392395
throw new IllegalStateException(
393-
"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
396+
"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]", ex);
394397
}
395398
}
396-
397-
// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations
398399
reader.loadBeanDefinitions(resource);
399400
});
400401
}

spring-context/src/main/java/org/springframework/context/annotation/ImportResource.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,14 +29,15 @@
2929
* Indicates one or more resources containing bean definitions to import.
3030
*
3131
* <p>Like {@link Import @Import}, this annotation provides functionality similar to
32-
* the {@code <import/>} element in Spring XML. It is typically used when designing
33-
* {@link Configuration @Configuration} classes to be bootstrapped by an
34-
* {@link AnnotationConfigApplicationContext}, but where some XML functionality such
35-
* as namespaces is still necessary.
32+
* the {@code <import/>} element in Spring XML configuration. It is typically used
33+
* when designing {@link Configuration @Configuration} classes to be bootstrapped by
34+
* an {@link AnnotationConfigApplicationContext}, but where some XML functionality
35+
* such as namespaces is still necessary.
3636
*
37-
* <p>By default, arguments to the {@link #value} attribute will be processed using a
37+
* <p>By default, arguments to the {@link #locations() locations} or {@link #value() value}
38+
* attribute will be processed using a
3839
* {@link org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader GroovyBeanDefinitionReader}
39-
* if ending in {@code ".groovy"}; otherwise, an
40+
* for resource locations ending in {@code ".groovy"}; otherwise, an
4041
* {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader XmlBeanDefinitionReader}
4142
* will be used to parse Spring {@code <beans/>} XML files. Optionally, the {@link #reader}
4243
* attribute may be declared, allowing the user to choose a custom {@link BeanDefinitionReader}
@@ -77,12 +78,19 @@
7778

7879
/**
7980
* {@link BeanDefinitionReader} implementation to use when processing
80-
* resources specified via the {@link #value} attribute.
81+
* resources specified via the {@link #locations() locations} or
82+
* {@link #value() value} attribute.
83+
* <p>The configured {@code BeanDefinitionReader} type must declare a
84+
* constructor that accepts a single
85+
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistry
86+
* BeanDefinitionRegistry} argument.
8187
* <p>By default, the reader will be adapted to the resource path specified:
8288
* {@code ".groovy"} files will be processed with a
83-
* {@link org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader GroovyBeanDefinitionReader};
84-
* whereas, all other resources will be processed with an
85-
* {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader XmlBeanDefinitionReader}.
89+
* {@link org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader
90+
* GroovyBeanDefinitionReader}; whereas, all other resources will be processed
91+
* with an {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader
92+
* XmlBeanDefinitionReader}.
93+
* @see #locations
8694
* @see #value
8795
*/
8896
Class<? extends BeanDefinitionReader> reader() default BeanDefinitionReader.class;
Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,27 +16,25 @@
1616

1717
package org.springframework.context.annotation.configuration;
1818

19-
import java.util.Collections;
20-
2119
import org.aspectj.lang.annotation.Aspect;
2220
import org.aspectj.lang.annotation.Before;
2321
import org.junit.jupiter.api.Test;
2422

2523
import org.springframework.aop.support.AopUtils;
2624
import org.springframework.beans.factory.annotation.Autowired;
2725
import org.springframework.beans.factory.annotation.Value;
26+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
2827
import org.springframework.beans.testfixture.beans.TestBean;
2928
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3029
import org.springframework.context.annotation.Bean;
3130
import org.springframework.context.annotation.Configuration;
3231
import org.springframework.context.annotation.ImportResource;
33-
import org.springframework.core.env.MapPropertySource;
34-
import org.springframework.core.env.PropertySource;
32+
import org.springframework.core.testfixture.env.MockPropertySource;
3533

3634
import static org.assertj.core.api.Assertions.assertThat;
3735

3836
/**
39-
* Integration tests for {@link ImportResource} support.
37+
* Integration tests for {@link ImportResource @ImportResource} support.
4038
*
4139
* @author Chris Beams
4240
* @author Juergen Hoeller
@@ -45,81 +43,88 @@
4543
class ImportResourceTests {
4644

4745
@Test
48-
void importXml() {
49-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlConfig.class);
50-
assertThat(ctx.containsBean("javaDeclaredBean")).as("did not contain java-declared bean").isTrue();
51-
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
52-
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
53-
assertThat(tb.getName()).isEqualTo("myName");
54-
ctx.close();
46+
void importResource() {
47+
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlConfig.class)) {
48+
assertThat(ctx.containsBean("javaDeclaredBean")).as("did not contain java-declared bean").isTrue();
49+
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
50+
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
51+
assertThat(tb.getName()).isEqualTo("myName");
52+
}
5553
}
5654

5755
@Test
58-
void importXmlIsInheritedFromSuperclassDeclarations() {
59-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(FirstLevelSubConfig.class);
60-
assertThat(ctx.containsBean("xmlDeclaredBean")).isTrue();
61-
ctx.close();
56+
void importResourceIsInheritedFromSuperclassDeclarations() {
57+
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(FirstLevelSubConfig.class)) {
58+
assertThat(ctx.containsBean("xmlDeclaredBean")).isTrue();
59+
}
6260
}
6361

6462
@Test
65-
void importXmlIsMergedFromSuperclassDeclarations() {
66-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SecondLevelSubConfig.class);
67-
assertThat(ctx.containsBean("secondLevelXmlDeclaredBean")).as("failed to pick up second-level-declared XML bean").isTrue();
68-
assertThat(ctx.containsBean("xmlDeclaredBean")).as("failed to pick up parent-declared XML bean").isTrue();
69-
ctx.close();
63+
void importResourceIsMergedFromSuperclassDeclarations() {
64+
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SecondLevelSubConfig.class)) {
65+
assertThat(ctx.containsBean("secondLevelXmlDeclaredBean")).as("failed to pick up second-level-declared XML bean").isTrue();
66+
assertThat(ctx.containsBean("xmlDeclaredBean")).as("failed to pick up parent-declared XML bean").isTrue();
67+
}
7068
}
7169

7270
@Test
73-
void importXmlWithNamespaceConfig() {
74-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithAopNamespaceConfig.class);
75-
Object bean = ctx.getBean("proxiedXmlBean");
76-
assertThat(AopUtils.isAopProxy(bean)).isTrue();
77-
ctx.close();
71+
void importResourceWithNamespaceConfig() {
72+
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithAopNamespaceConfig.class)) {
73+
Object bean = ctx.getBean("proxiedXmlBean");
74+
assertThat(AopUtils.isAopProxy(bean)).isTrue();
75+
}
7876
}
7977

8078
@Test
81-
void importXmlWithOtherConfigurationClass() {
82-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithConfigurationClass.class);
83-
assertThat(ctx.containsBean("javaDeclaredBean")).as("did not contain java-declared bean").isTrue();
84-
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
85-
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
86-
assertThat(tb.getName()).isEqualTo("myName");
87-
ctx.close();
79+
void importResourceWithOtherConfigurationClass() {
80+
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlWithConfigurationClass.class)) {
81+
assertThat(ctx.containsBean("javaDeclaredBean")).as("did not contain java-declared bean").isTrue();
82+
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
83+
TestBean tb = ctx.getBean("javaDeclaredBean", TestBean.class);
84+
assertThat(tb.getName()).isEqualTo("myName");
85+
}
8886
}
8987

9088
@Test
9189
void importWithPlaceholder() {
92-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
93-
PropertySource<?> propertySource = new MapPropertySource("test",
94-
Collections.<String, Object> singletonMap("test", "springframework"));
95-
ctx.getEnvironment().getPropertySources().addFirst(propertySource);
96-
ctx.register(ImportXmlConfig.class);
97-
ctx.refresh();
98-
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
99-
ctx.close();
90+
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext()) {
91+
ctx.getEnvironment().getPropertySources().addFirst(new MockPropertySource("test").withProperty("test", "springframework"));
92+
ctx.register(ImportXmlConfig.class);
93+
ctx.refresh();
94+
assertThat(ctx.containsBean("xmlDeclaredBean")).as("did not contain xml-declared bean").isTrue();
95+
}
10096
}
10197

10298
@Test
103-
void importXmlWithAutowiredConfig() {
104-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlAutowiredConfig.class);
105-
String name = ctx.getBean("xmlBeanName", String.class);
106-
assertThat(name).isEqualTo("xml.declared");
107-
ctx.close();
99+
void importResourceWithAutowiredConfig() {
100+
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportXmlAutowiredConfig.class)) {
101+
String name = ctx.getBean("xmlBeanName", String.class);
102+
assertThat(name).isEqualTo("xml.declared");
103+
}
108104
}
109105

110106
@Test
111107
void importNonXmlResource() {
112-
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportNonXmlResourceConfig.class);
113-
assertThat(ctx.containsBean("propertiesDeclaredBean")).isTrue();
114-
ctx.close();
108+
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportNonXmlResourceConfig.class)) {
109+
assertThat(ctx.containsBean("propertiesDeclaredBean")).isTrue();
110+
}
111+
}
112+
113+
@Test
114+
void importResourceWithPrivateReader() {
115+
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ImportWithPrivateReaderConfig.class)) {
116+
assertThat(ctx.containsBean("propertiesDeclaredBean")).isTrue();
117+
}
115118
}
116119

117120

118121
@Configuration
119122
@ImportResource("classpath:org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml")
120123
static class ImportXmlConfig {
124+
121125
@Value("${name}")
122126
private String name;
127+
123128
@Bean public TestBean javaDeclaredBean() {
124129
return new TestBean(this.name);
125130
}
@@ -146,6 +151,7 @@ static class ImportXmlWithAopNamespaceConfig {
146151

147152
@Aspect
148153
static class AnAspect {
154+
149155
@Before("execution(* org.springframework.beans.testfixture.beans.TestBean.*(..))")
150156
public void advice() { }
151157
}
@@ -158,18 +164,37 @@ static class ImportXmlWithConfigurationClass {
158164
@Configuration
159165
@ImportResource("classpath:org/springframework/context/annotation/configuration/ImportXmlConfig-context.xml")
160166
static class ImportXmlAutowiredConfig {
161-
@Autowired TestBean xmlDeclaredBean;
162167

163-
@Bean public String xmlBeanName() {
168+
@Autowired
169+
TestBean xmlDeclaredBean;
170+
171+
@Bean
172+
public String xmlBeanName() {
164173
return xmlDeclaredBean.getName();
165174
}
166175
}
167176

168177
@SuppressWarnings("deprecation")
169178
@Configuration
170-
@ImportResource(locations = "classpath:org/springframework/context/annotation/configuration/ImportNonXmlResourceConfig-context.properties",
179+
@ImportResource(locations = "org/springframework/context/annotation/configuration/ImportNonXmlResourceConfig.properties",
171180
reader = org.springframework.beans.factory.support.PropertiesBeanDefinitionReader.class)
172181
static class ImportNonXmlResourceConfig {
173182
}
174183

184+
@SuppressWarnings("deprecation")
185+
@Configuration
186+
@ImportResource(locations = "org/springframework/context/annotation/configuration/ImportNonXmlResourceConfig.properties",
187+
reader = PrivatePropertiesBeanDefinitionReader.class)
188+
static class ImportWithPrivateReaderConfig {
189+
}
190+
191+
@SuppressWarnings("deprecation")
192+
private static class PrivatePropertiesBeanDefinitionReader
193+
extends org.springframework.beans.factory.support.PropertiesBeanDefinitionReader {
194+
195+
PrivatePropertiesBeanDefinitionReader(BeanDefinitionRegistry registry) {
196+
super(registry);
197+
}
198+
}
199+
175200
}

0 commit comments

Comments
 (0)