Understanding Spring Framework Core Interfaces and Bean Lifecycle from Source Code
This article explains the core interfaces of the Spring Framework, such as BeanDefinition, BeanFactory, ApplicationContext, and BeanPostProcessor, and walks through the bean creation process—including refresh, preInstantiateSingletons, getBean, and the three‑level singleton caches—providing detailed code examples and insights into how Spring resolves circular dependencies.
Preface
The Spring family is powerful but complex; its core functionality boils down to two concepts: IoC and AOP. This article explains important interfaces in the Spring container and how Spring resolves circular dependencies from a source‑code perspective (Spring version 5.2.2.RELEASE).
Core Interfaces
BeanDefinition
BeanDefinitionis a crucial interface defined in the spring-beans module. It describes a bean instance, including property values, constructor arguments, and additional metadata.
/**
* A BeanDefinition describes a bean instance, which has property values,
* constructor argument values, and further information supplied by
* concrete implementations.
*
* <p>This is just a minimal interface: The main intention is to allow a
* {@link BeanFactoryPostProcessor} to introspect and modify property values
* and other bean metadata.
*/
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
}In essence, BeanDefinition abstracts the metadata of a Spring bean, similar to how java.lang.Class abstracts metadata of a Java class.
BeanFactory
BeanFactoryis the top‑level interface of the Spring container, also defined in spring-beans. Its most important method is getBean.
/** From Spring container get bean instance */
<T> T getBean(Class<T> requiredType) throws BeansException;
Object getBean(String name) throws BeansException;ApplicationContext
ApplicationContextextends BeanFactory and adds features such as resource loading, event publishing, internationalization, and hierarchical contexts. It is defined in the spring-context module.
/** Central interface to provide configuration for an application. */
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
}BeanPostProcessor
BeanPostProcessorallows custom modification of new bean instances, e.g., wrapping beans with proxies.
/** Factory hook that allows for custom modification of new bean instances —
* for example, checking for marker interfaces or wrapping beans with proxies.
*/
public interface BeanPostProcessor {
}Starting Spring
Starting a Spring container can be done with just a few lines of code:
public class SpringBeanLifecycle {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
applicationContext.getBean(Student.class);
}
}The XML configuration ( applicationContext.xml) defines a bean named student of class com.sicimike.bean.lifecycle.Student.
Refresh Process
The refresh method contains the complete startup logic of the Spring container. Its core steps include preparing the context, obtaining a fresh bean factory, processing bean factory post‑processors, registering bean post‑processors, initializing message sources, event multicaster, singleton beans, and finally publishing a refresh event.
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
} catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
destroyBeans();
cancelRefresh(ex);
throw ex;
} finally {
resetCommonCaches();
}
}
}Finishing BeanFactory Initialization
The method finishBeanFactoryInitialization triggers the creation of all non‑lazy singleton beans, which ultimately calls preInstantiateSingletons.
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// conversion service, embedded value resolver, LoadTimeWeaverAware, etc.
beanFactory.preInstantiateSingletons();
}preInstantiateSingletons
This method iterates over bean definitions and creates each non‑abstract, singleton, non‑lazy bean.
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit = (factory instanceof SmartFactoryBean) && ((SmartFactoryBean<?>) factory).isEagerInit();
if (isEagerInit) {
getBean(beanName);
}
}
} else {
getBean(beanName);
}
}
}
// post‑initialization callbacks for SmartInitializingSingleton beans omitted for brevity
}getBean and doGetBean
The public getBean(String name) delegates to doGetBean, which handles singleton cache lookup, circular reference checks, parent factory delegation, and bean creation.
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
return (T) getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// ... handle prototype, circular references, etc.
// create bean if necessary
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> createBean(beanName, mbd, args));
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
// prototype handling omitted for brevity
}
// type checking omitted for brevity
return (T) bean;
}Singleton Caches
Spring uses three caches to resolve circular dependencies:
Level‑1 cache ( singletonObjects): fully initialized singleton instances.
Level‑2 cache ( earlySingletonObjects): partially created beans exposed early to break cycles.
Level‑3 cache ( singletonFactories): factories that can create the early reference.
// Level‑1 cache
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// Level‑2 cache
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// Level‑3 cache
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);The method getSingleton(String beanName, boolean allowEarlyReference) first checks the Level‑1 cache, then the Level‑2 cache, and finally creates an early reference via the Level‑3 factory if needed.
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
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);
}
}
}
}
return singletonObject;
}createBean and doCreateBean
createBeanis the entry point for actual bean instantiation. It resolves the bean class, applies method overrides, allows BeanPostProcessors to return a proxy, and finally delegates to doCreateBean.
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass()) {
mbd = new RootBeanDefinition(mbd);
mbd.setBeanClass(resolvedClass);
}
mbd.prepareMethodOverrides();
Object bean = resolveBeforeInstantiation(beanName, mbd);
if (bean != null) return bean;
return doCreateBean(beanName, mbd, args);
} doCreateBeanperforms three main steps: create the bean instance, populate its properties, and initialize it (including applying BeanPostProcessors and init methods). If the bean is a singleton and circular references are allowed, it registers a singleton factory for early exposure.
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] args) throws BeanCreationException {
// 1. Instantiate the bean
BeanWrapper instanceWrapper = mbd.isSingleton() ? this.factoryBeanInstanceCache.remove(beanName) : null;
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
// 2. Early singleton exposure for circular references
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 3. Populate properties and initialize bean
populateBean(beanName, mbd, instanceWrapper);
Object exposedObject = initializeBean(beanName, bean, mbd);
// 4. Register disposable bean if necessary
registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
}Conclusion
The Spring source code is massive, but by focusing on specific problems—such as how Spring resolves circular dependencies—you can gradually understand its inner workings. The three‑level singleton cache and early exposure of bean references are the key mechanisms that enable Spring to handle circular references for singleton beans.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
