Introduce callback for singleton availability
Closes gh-21362
This commit is contained in:
parent
1a8d64f9d8
commit
c9e85ec297
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.beans.factory.config;
|
package org.springframework.beans.factory.config;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -57,6 +59,17 @@ public interface SingletonBeanRegistry {
|
||||||
*/
|
*/
|
||||||
void registerSingleton(String beanName, Object singletonObject);
|
void registerSingleton(String beanName, Object singletonObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a callback to be triggered when the specified singleton becomes available
|
||||||
|
* in the bean registry.
|
||||||
|
* @param beanName the name of the bean
|
||||||
|
* @param singletonConsumer a callback for reacting to the availability of the freshly
|
||||||
|
* registered/created singleton instance (intended for follow-up steps before the bean is
|
||||||
|
* actively used by other callers, not for modifying the given singleton instance itself)
|
||||||
|
* @since 6.2
|
||||||
|
*/
|
||||||
|
void addSingletonCallback(String beanName, Consumer<Object> singletonConsumer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the (raw) singleton object registered under the given name.
|
* Return the (raw) singleton object registered under the given name.
|
||||||
* <p>Only checks already instantiated singletons; does not return an Object
|
* <p>Only checks already instantiated singletons; does not return an Object
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
|
@ -27,6 +26,7 @@ import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
||||||
|
|
@ -79,8 +79,11 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||||
/** Cache of singleton objects: bean name to bean instance. */
|
/** Cache of singleton objects: bean name to bean instance. */
|
||||||
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
|
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
|
||||||
|
|
||||||
/** Cache of singleton factories: bean name to ObjectFactory. */
|
/** Creation-time registry of singleton factories: bean name to ObjectFactory. */
|
||||||
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
|
private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);
|
||||||
|
|
||||||
|
/** Custom callbacks for singleton creation/registration. */
|
||||||
|
private final Map<String, Consumer<Object>> singletonCallbacks = new ConcurrentHashMap<>(16);
|
||||||
|
|
||||||
/** Cache of early singleton objects: bean name to bean instance. */
|
/** Cache of early singleton objects: bean name to bean instance. */
|
||||||
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
|
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
|
||||||
|
|
@ -133,8 +136,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the given singleton object to the singleton cache of this factory.
|
* Add the given singleton object to the singleton registry.
|
||||||
* <p>To be called for eager registration of singletons.
|
* <p>To be called for exposure of freshly registered/created singletons.
|
||||||
* @param beanName the name of the bean
|
* @param beanName the name of the bean
|
||||||
* @param singletonObject the singleton object
|
* @param singletonObject the singleton object
|
||||||
*/
|
*/
|
||||||
|
|
@ -147,12 +150,17 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||||
this.singletonFactories.remove(beanName);
|
this.singletonFactories.remove(beanName);
|
||||||
this.earlySingletonObjects.remove(beanName);
|
this.earlySingletonObjects.remove(beanName);
|
||||||
this.registeredSingletons.add(beanName);
|
this.registeredSingletons.add(beanName);
|
||||||
|
|
||||||
|
Consumer<Object> callback = this.singletonCallbacks.get(beanName);
|
||||||
|
if (callback != null) {
|
||||||
|
callback.accept(singletonObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the given singleton factory for building the specified singleton
|
* Add the given singleton factory for building the specified singleton
|
||||||
* if necessary.
|
* if necessary.
|
||||||
* <p>To be called for eager registration of singletons, e.g. to be able to
|
* <p>To be called for early exposure purposes, e.g. to be able to
|
||||||
* resolve circular references.
|
* resolve circular references.
|
||||||
* @param beanName the name of the bean
|
* @param beanName the name of the bean
|
||||||
* @param singletonFactory the factory for the singleton object
|
* @param singletonFactory the factory for the singleton object
|
||||||
|
|
@ -164,6 +172,11 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||||
this.registeredSingletons.add(beanName);
|
this.registeredSingletons.add(beanName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addSingletonCallback(String beanName, Consumer<Object> singletonConsumer) {
|
||||||
|
this.singletonCallbacks.put(beanName, singletonConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Object getSingleton(String beanName) {
|
public Object getSingleton(String beanName) {
|
||||||
|
|
@ -262,6 +275,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.singletonsCurrentlyInDestruction) {
|
if (this.singletonsCurrentlyInDestruction) {
|
||||||
throw new BeanCreationNotAllowedException(beanName,
|
throw new BeanCreationNotAllowedException(beanName,
|
||||||
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
|
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
|
||||||
|
|
@ -343,8 +357,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the bean with the given name from the singleton cache of this factory,
|
* Remove the bean with the given name from the singleton registry, either on
|
||||||
* to be able to clean up eager registration of a singleton if creation failed.
|
* regular destruction or on cleanup after early exposure when creation failed.
|
||||||
* @param beanName the name of the bean
|
* @param beanName the name of the bean
|
||||||
*/
|
*/
|
||||||
protected void removeSingleton(String beanName) {
|
protected void removeSingleton(String beanName) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 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.
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.beans.testfixture.beans.DerivedTestBean;
|
import org.springframework.beans.testfixture.beans.DerivedTestBean;
|
||||||
|
|
@ -35,12 +37,18 @@ class DefaultSingletonBeanRegistryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void singletons() {
|
void singletons() {
|
||||||
|
AtomicBoolean tbFlag = new AtomicBoolean();
|
||||||
|
beanRegistry.addSingletonCallback("tb", instance -> tbFlag.set(true));
|
||||||
TestBean tb = new TestBean();
|
TestBean tb = new TestBean();
|
||||||
beanRegistry.registerSingleton("tb", tb);
|
beanRegistry.registerSingleton("tb", tb);
|
||||||
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
||||||
|
assertThat(tbFlag.get()).isTrue();
|
||||||
|
|
||||||
|
AtomicBoolean tb2Flag = new AtomicBoolean();
|
||||||
|
beanRegistry.addSingletonCallback("tb2", instance -> tb2Flag.set(true));
|
||||||
TestBean tb2 = (TestBean) beanRegistry.getSingleton("tb2", TestBean::new);
|
TestBean tb2 = (TestBean) beanRegistry.getSingleton("tb2", TestBean::new);
|
||||||
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
|
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
|
||||||
|
assertThat(tb2Flag.get()).isTrue();
|
||||||
|
|
||||||
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
assertThat(beanRegistry.getSingleton("tb")).isSameAs(tb);
|
||||||
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
|
assertThat(beanRegistry.getSingleton("tb2")).isSameAs(tb2);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue