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 7 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 @@ -990,9 +990,17 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
* that we couldn't obtain a shortcut FactoryBean instance
*/
private @Nullable FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
boolean locked = this.singletonLock.tryLock();
if (!locked) {
return null;
Boolean lockFlag = isCurrentThreadAllowedToHoldSingletonLock();
if (lockFlag == 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 {

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

@ -269,13 +269,15 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements @@ -269,13 +269,15 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
// 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
// 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();
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);
}
finally {

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

@ -1,5 +1,5 @@ @@ -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");
* you may not use this file except in compliance with the License.
@ -117,7 +117,15 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg @@ -117,7 +117,15 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
*/
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
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 {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
@ -130,11 +138,13 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg @@ -130,11 +138,13 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
}
else {
if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet
return object;
if (locked) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet
return object;
}
beforeSingletonCreation(beanName);
}
beforeSingletonCreation(beanName);
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
@ -143,7 +153,9 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg @@ -143,7 +153,9 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
afterSingletonCreation(beanName);
if (locked) {
afterSingletonCreation(beanName);
}
}
}
if (containsSingleton(beanName)) {
@ -154,7 +166,9 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg @@ -154,7 +166,9 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
return object;
}
finally {
this.singletonLock.unlock();
if (locked) {
this.singletonLock.unlock();
}
}
}
else {

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

@ -24,6 +24,7 @@ import org.junit.jupiter.api.Timeout; @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@ -243,14 +244,24 @@ class BackgroundBootstrapTests { @@ -243,14 +244,24 @@ class BackgroundBootstrapTests {
}
@Bean
public TestBean testBean4() {
public FactoryBean<TestBean> testBean4() {
try {
Thread.sleep(2000);
}
catch (InterruptedException ex) {
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