Browse Source

Merge branch '6.2.x'

# Conflicts:
#	spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
pull/34914/head
Juergen Hoeller 9 months ago
parent
commit
1187bc2016
  1. 14
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
  2. 12
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java
  3. 30
      spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java
  4. 15
      spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java

14
spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

@ -990,9 +990,17 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
* that we couldn't obtain a shortcut FactoryBean instance * that we couldn't obtain a shortcut FactoryBean instance
*/ */
private @Nullable FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) { private @Nullable FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
boolean locked = this.singletonLock.tryLock(); Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock();
if (!locked) { if (lockFlag == null) {
return null; this.singletonLock.lock();
}
else {
boolean locked = (lockFlag && this.singletonLock.tryLock());
if (!locked) {
// Avoid shortcut FactoryBean instance but allow for subsequent type-based resolution.
resolveBeanClass(mbd, beanName);
return null;
}
} }
try { try {

12
spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java

@ -269,13 +269,15 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
// Fallback as of 6.2: process given singleton bean outside of singleton lock. // Fallback as of 6.2: process given singleton bean outside of singleton lock.
// Thread-safe exposure is still guaranteed, there is just a risk of collisions // Thread-safe exposure is still guaranteed, there is just a risk of collisions
// when triggering creation of other beans as dependencies of the current bean. // when triggering creation of other beans as dependencies of the current bean.
if (logger.isInfoEnabled()) {
logger.info("Obtaining singleton bean '" + beanName + "' in thread \"" +
Thread.currentThread().getName() + "\" while other thread holds " +
"singleton lock for other beans " + this.singletonsCurrentlyInCreation);
}
this.lenientCreationLock.lock(); this.lenientCreationLock.lock();
try { try {
if (logger.isInfoEnabled()) {
Set<String> lockedBeans = new HashSet<>(this.singletonsCurrentlyInCreation);
lockedBeans.removeAll(this.singletonsInLenientCreation);
logger.info("Obtaining singleton bean '" + beanName + "' in thread \"" +
currentThread.getName() + "\" while other thread holds singleton " +
"lock for other beans " + lockedBeans);
}
this.singletonsInLenientCreation.add(beanName); this.singletonsInLenientCreation.add(beanName);
} }
finally { finally {

30
spring-beans/src/main/java/org/springframework/beans/factory/support/FactoryBeanRegistrySupport.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -117,7 +117,15 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
*/ */
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && containsSingleton(beanName)) { if (factory.isSingleton() && containsSingleton(beanName)) {
this.singletonLock.lock(); Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock();
boolean locked;
if (lockFlag == null) {
this.singletonLock.lock();
locked = true;
}
else {
locked = (lockFlag && this.singletonLock.tryLock());
}
try { try {
Object object = this.factoryBeanObjectCache.get(beanName); Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) { if (object == null) {
@ -130,11 +138,13 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
} }
else { else {
if (shouldPostProcess) { if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) { if (locked) {
// Temporarily return non-post-processed object, not storing it yet if (isSingletonCurrentlyInCreation(beanName)) {
return object; // Temporarily return non-post-processed object, not storing it yet
return object;
}
beforeSingletonCreation(beanName);
} }
beforeSingletonCreation(beanName);
try { try {
object = postProcessObjectFromFactoryBean(object, beanName); object = postProcessObjectFromFactoryBean(object, beanName);
} }
@ -143,7 +153,9 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
"Post-processing of FactoryBean's singleton object failed", ex); "Post-processing of FactoryBean's singleton object failed", ex);
} }
finally { finally {
afterSingletonCreation(beanName); if (locked) {
afterSingletonCreation(beanName);
}
} }
} }
if (containsSingleton(beanName)) { if (containsSingleton(beanName)) {
@ -154,7 +166,9 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
return object; return object;
} }
finally { finally {
this.singletonLock.unlock(); if (locked) {
this.singletonLock.unlock();
}
} }
} }
else { else {

15
spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java

@ -24,6 +24,7 @@ import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException; import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@ -243,14 +244,24 @@ class BackgroundBootstrapTests {
} }
@Bean @Bean
public TestBean testBean4() { public FactoryBean<TestBean> testBean4() {
try { try {
Thread.sleep(2000); Thread.sleep(2000);
} }
catch (InterruptedException ex) { catch (InterruptedException ex) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
return new TestBean(); TestBean testBean = new TestBean();
return new FactoryBean<>() {
@Override
public TestBean getObject() {
return testBean;
}
@Override
public Class<?> getObjectType() {
return testBean.getClass();
}
};
} }
} }

Loading…
Cancel
Save