Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid getting objects that have not completed initialization #26376

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -181,21 +181,18 @@ protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
synchronized (this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject != null || !isSingletonCurrentlyInCreation(beanName)) {
return singletonObject;
}
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
Expand Down
Expand Up @@ -17,11 +17,17 @@
package org.springframework.beans.factory.support;

import org.junit.jupiter.api.Test;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.testfixture.beans.DerivedTestBean;
import org.springframework.beans.testfixture.beans.Father;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Assert;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

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

Expand Down Expand Up @@ -101,4 +107,58 @@ public void testDependentRegistration() {
assertThat(beanRegistry.isDependent("c", "c")).isTrue();
}

@Test
public void testGetLazySingleton() throws InterruptedException {
testGetSingleton("father");
}

@Test
public void testGetNonLazySingleton() throws InterruptedException {
testGetSingleton("father-non-lazy");
}

private void testGetSingleton(String fatherBeanName) throws InterruptedException {

CountDownLatch countDownLatch = new CountDownLatch(2);
AtomicInteger occurExceptionCount = new AtomicInteger(0);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new ClassPathResource("DefaultSingletonBeanRegistryTests.xml", getClass()));

// Just for testing, you should use a thread pool
new Thread(() -> {
Father father = beanFactory.getBean(fatherBeanName, Father.class);
try {
Assert.notNull(father, "father Shouldn't be null");
Assert.notNull(father.getSon(), "son Shouldn't be null");
} catch (Exception e) {
occurExceptionCount.incrementAndGet();
System.out.println(Thread.currentThread().getName());
e.printStackTrace();
}
countDownLatch.countDown();
}, "first-thread").start();

Thread.sleep(1000);

// Just for testing, you should use a thread pool
new Thread(() -> {
Father father = beanFactory.getBean(fatherBeanName, Father.class);
try {
Assert.notNull(father, "father Shouldn't be null");
Assert.notNull(father.getSon(), "son Shouldn't be null");
} catch (Exception e) {
occurExceptionCount.incrementAndGet();
System.out.println(Thread.currentThread().getName());
e.printStackTrace();
}
countDownLatch.countDown();
}, "second-thread").start();

while (countDownLatch.getCount() != 0) {

}
Assert.isTrue(occurExceptionCount.get() == 0, "Throwing an exception in the sub-thread that gets the bean");
}

}
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.1.xsd">

<bean id="mother" class="org.springframework.beans.testfixture.beans.Mother" lazy-init="true">
</bean>

<bean id="father" class="org.springframework.beans.testfixture.beans.Father" lazy-init="true">
<property name="son" ref="son"/>
</bean>

<bean id="son" class="org.springframework.beans.testfixture.beans.Son" lazy-init="true">
<property name="father" ref="father"/>
<property name="mother" ref="mother"/>
</bean>


<bean id="mother-non-lazy" class="org.springframework.beans.testfixture.beans.Mother" lazy-init="false">
</bean>

<bean id="father-non-lazy" class="org.springframework.beans.testfixture.beans.Father" lazy-init="false">
<property name="son" ref="son-non-lazy"/>
</bean>

<bean id="son-non-lazy" class="org.springframework.beans.testfixture.beans.Son" lazy-init="false">
<property name="father" ref="father-non-lazy"/>
<property name="mother" ref="mother-non-lazy"/>
</bean>


</beans>
@@ -0,0 +1,17 @@
package org.springframework.beans.testfixture.beans;

/**
* @author jinxiong
*/
public class Father {

private Son son;

public Son getSon() {
return son;
}

public void setSon(Son son) {
this.son = son;
}
}
@@ -0,0 +1,18 @@
package org.springframework.beans.testfixture.beans;

/**
* @author jinxiong
*/
public class Mother {

public Mother() {

// Simulation takes a long time、just for testing
try {
Thread.sleep(4 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}
@@ -0,0 +1,27 @@
package org.springframework.beans.testfixture.beans;

/**
* @author jinxiong
*/
public class Son {

private Father father;

private Mother mother;

public Father getFather() {
return father;
}

public void setFather(Father father) {
this.father = father;
}

public Mother getMother() {
return mother;
}

public void setMother(Mother mother) {
this.mother = mother;
}
}