Merge branch '7.0.x'

# Conflicts:
#	framework-platform/framework-platform.gradle
This commit is contained in:
Juergen Hoeller
2026-05-27 16:35:51 +02:00
6 changed files with 75 additions and 11 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.21.2"))
api(platform("io.micrometer:micrometer-bom:1.16.5"))
api(platform("io.netty:netty-bom:4.2.13.Final"))
api(platform("io.netty:netty-bom:4.2.14.Final"))
api(platform("io.projectreactor:reactor-bom:2025.0.5"))
api(platform("io.rsocket:rsocket-bom:1.1.5"))
api(platform("org.apache.groovy:groovy-bom:5.0.6"))
@@ -120,7 +120,7 @@ dependencies {
api("org.glassfish:jakarta.el:4.0.2")
api("org.graalvm.sdk:graal-sdk:22.3.1")
api("org.hamcrest:hamcrest:3.0")
api("org.hibernate.orm:hibernate-core:7.3.4.Final")
api("org.hibernate.orm:hibernate-core:7.3.6.Final")
api("org.hibernate.validator:hibernate-validator:9.1.0.Final")
api("org.hsqldb:hsqldb:2.7.4")
api("org.htmlunit:htmlunit:4.21.0")
@@ -1154,12 +1154,19 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (mbd.isBackgroundInit()) {
Executor executor = getBootstrapExecutor();
if (executor != null) {
// Force initialization of depends-on beans in mainline thread.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
getBean(dep);
}
}
// Force initialization of factory reference in mainline thread.
String factoryBeanName = mbd.getFactoryBeanName();
if (factoryBeanName != null) {
getBean(factoryBeanName);
}
// Instantiate current bean in background thread.
CompletableFuture<?> future = CompletableFuture.runAsync(
() -> instantiateSingletonInBackgroundThread(beanName), executor);
addSingletonFactory(beanName, () -> {
@@ -260,9 +260,11 @@ class ConfigurationClassParser {
return;
}
else if (configClass.isScanned()) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
if (existingClass.isImported()) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
}
// An implicitly scanned bean definition should not override an explicit import.
return;
@@ -197,6 +197,16 @@ class BackgroundBootstrapTests {
}
}
@Test
@Timeout(10)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithCustomExecutorAndLazyConfig() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CustomExecutorLazyBeanConfig.class);
assertThat(ctx.getBeanFactory().containsSingleton("testBean1")).isTrue();
assertThat(ctx.getBeanFactory().containsSingleton("testBean2")).isTrue();
ctx.close();
}
@Configuration(proxyBeanMethods = false)
static class UnmanagedThreadBeanConfig {
@@ -542,4 +552,36 @@ class BackgroundBootstrapTests {
}
}
@Configuration(proxyBeanMethods = false)
static class CustomExecutorLazyBeanConfig {
@Bean
public ThreadPoolTaskExecutor bootstrapExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("Custom-");
executor.setCorePoolSize(2);
executor.initialize();
return executor;
}
@Configuration(proxyBeanMethods = false)
@Lazy
static class LazyBeanConfig {
@Bean(bootstrap = BACKGROUND)
public TestBean testBean1() throws InterruptedException {
Thread.sleep(6000);
return new TestBean();
}
@Bean(bootstrap = BACKGROUND)
@Lazy
public TestBean testBean2() throws InterruptedException {
Thread.sleep(6000);
return new TestBean();
}
}
}
}
@@ -26,6 +26,7 @@ import example.scannable.CustomComponent;
import example.scannable.CustomStereotype;
import example.scannable.DefaultNamedComponent;
import example.scannable.FooService;
import example.scannable.FooServiceImpl;
import example.scannable.MessageBean;
import example.scannable.ScopedProxyTestBean;
import example.scannable_implicitbasepackage.ComponentScanAnnotatedConfigWithImplicitBasePackage;
@@ -43,6 +44,7 @@ import org.springframework.beans.factory.annotation.CustomAutowireConfigurer;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
@@ -84,6 +86,17 @@ class ComponentScanAnnotationIntegrationTests {
assertContextContainsBean(ctx, "fooServiceImpl");
}
@Test
void controlScanWithExplicitRegistration() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.registerBeanDefinition("myFooService", new RootBeanDefinition(FooServiceImpl.class));
ctx.scan(example.scannable.PackageMarker.class.getPackage().getName());
ctx.refresh();
assertContextContainsBean(ctx, "myFooService");
assertContextContainsBean(ctx, "fooServiceImpl");
}
@Test
void viaContextRegistration() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ComponentScanAnnotatedConfig.class);
@@ -117,8 +117,8 @@ public class MethodParameter {
* return type; 0 for the first method parameter; 1 for the second method
* parameter, etc.
* @param nestingLevel the nesting level of the target type
* (typically 1; for example, in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* (1 for the top-level type; in case of a List of Lists, 2 would indicate
* the nested List, whereas 3 would indicate the element of the nested List)
*/
public MethodParameter(Method method, int parameterIndex, int nestingLevel) {
Assert.notNull(method, "Method must not be null");
@@ -142,8 +142,8 @@ public class MethodParameter {
* @param constructor the Constructor to specify a parameter for
* @param parameterIndex the index of the parameter
* @param nestingLevel the nesting level of the target type
* (typically 1; for example, in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* (1 for the top-level type; in case of a List of Lists, 2 would indicate
* the nested List, whereas 3 would indicate the element of the nested List)
*/
public MethodParameter(Constructor<?> constructor, int parameterIndex, int nestingLevel) {
Assert.notNull(constructor, "Constructor must not be null");
@@ -292,8 +292,8 @@ public class MethodParameter {
/**
* Return the nesting level of the target type
* (typically 1; for example, in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List).
* (1 for the top-level type; in case of a List of Lists, 2 would indicate
* the nested List, whereas 3 would indicate the element of the nested List).
*/
public int getNestingLevel() {
return this.nestingLevel;