Table of contents
Open Table of contents
Article body
A question occurred to me today: when does Spring instantiate a bean?
-
When the Spring container starts?
-
Or when the bean is first used?
I decided to inspect the source code.
Years ago, before Spring became as powerful as it is today, how did we use it? We configured it with XML, like this:
public class SchoolServiceImpl {
public String getTimeStr(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:MM:ss"));
}
public String getInStr(String str){
return str;
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="schoolService" class="com.learn.spring.spring01.service.SchoolServiceImpl">
</bean>
</beans>
Register SchoolServiceImpl with Spring so Spring can manage it. How is this done? As follows:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("schoolService.xml");
SchoolServiceImpl service = context.getBean("schoolService", SchoolServiceImpl.class);
System.out.println(service.getTimeStr());
}
Start debugging.
The main steps:
- Create a BeanFactory instance, DefaultListableBeanFactory. This object manages all beans; I will explain below why we can say it manages every bean object.
refresh()->obtainFreshBeanFactory->refreshBeanFactory->DefaultListableBeanFactory beanFactory = this.createBeanFactory();
- Read and parse the XML files at the configured paths, then register beans.
loadBeanDefinitions(beanFactory) -> create an XmlBeanDefinitionReader to read XML -> loadBeanDefinitions(beanDefinitionReader) -> loadBeanDefinitions(configLocations), reading the XML locations specified in the code above; multiple files are parsed in a loop -> XmlBeanDefinitionReader further processes the Resource objects it reads -> doLoadBeanDefinitions does two things: parse XML into a Document and register beans -> DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions.parseBeanDefinitions parses each bean, import, and other element. Its parseDefaultElement method is:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
-> processBeanDefinition parses each bean element into a BeanDefinitionHolder, which already contains the abstract BeanDefinition. This object is important: it stores whether the bean is singleton or prototype, whether it is lazy-loaded, and so on. Later bean instantiation retrieves this BeanDefinition to create the instance. registerBeanDefinition registers it -> registerBeanDefinition in DefaultListableBeanFactory checks whether a BeanDefinition for this bean already exists by looking in beanDefinitionMap. If not, it puts beanName and BeanDefinition into that map and records the name in the beanDefinitionNames list.
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
@Nullable
private static Class<?> javaxInjectProviderClass;
private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories;
@Nullable
private String serializationId;
private boolean allowBeanDefinitionOverriding = true;
private boolean allowEagerClassLoading = true;
@Nullable
private Comparator<Object> dependencyComparator;
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap(16);
// Cache it in this map.
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap(256);
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap(64);
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap(64);
private volatile List<String> beanDefinitionNames = new ArrayList(256);
private volatile Set<String> manualSingletonNames = new LinkedHashSet(16);
@Nullable
private volatile String[] frozenBeanDefinitionNames;
private volatile boolean configurationFrozen = false;
After each bean is registered, its BeanDefinition is stored in beanDefinitionMap in DefaultListableBeanFactory. This is why we can say the BeanFactory object manages all beans.
- Create the bean, populate its properties, and initialize it.
The basic process of instantiating an object is:
// 1. Create entity, an instance of SchoolEntity.
SchoolEntity entity = new SchoolEntity();
// 2. Populate the properties of entity.
entity.setName("jack");
entity.setAge(12);
// 3. Use entity to do something.
Use entity...
System.out.println(entity.getName);
The Spring flow is:
After obtainFreshBeanFactory in Step 1 finishes, execution eventually reaches finishBeanFactoryInitialization(beanFactory) -> preInstantiateSingletons -> this.getBean (this iterates over all beanNames in beanDefinitionNames to retrieve the beans
) -> doGetBean -> this.getSingleton(beanName). The bean does not yet exist, so it is created ->
this.createBean -> this.doCreateBean -> after creation, the bean is stored in the singletonObjects map of DefaultSingletonBeanRegistry.
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
} catch (IllegalStateException var16) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw var16;
}
} catch (BeanCreationException var17) {
BeanCreationException ex = var17;
if (recordSuppressedExceptions) {
Iterator var8 = this.suppressedExceptions.iterator();
while(var8.hasNext()) {
Exception suppressedException = (Exception)var8.next();
ex.addRelatedCause(suppressedException);
}
}
throw ex;
} finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
this.afterSingletonCreation(beanName);
}
// Add it to the map.
if (newSingleton) {
this.addSingleton(beanName, singletonObject);
}
this.doCreateBean (create the bean object) -> this.populateBean (populate its properties) -> this.initializeBean (initialize the bean).
The official documentation already explains these steps concisely and clearly, and recommends registering beans as early as possible.

A singleton without lazy-init configuration is created ahead of time when the Spring container starts. A prototype bean is created when it is used.

UsefulLink: