preface

Today with everyone together into the spring source reading stage, we will be from a familiar with advanced container – ClassPathXmlApplicationContext for entrance to understand the spring container loading process.

IOC container loading

ClassPathXmlApplicationContext many constructors, we found the entrance really call.Copy the code
public ClassPathXmlApplicationContext(String[] paths, Class<? > clazz, @nullable ApplicationContext parent) throws BeansException {// Super (parent); Assert.notNull(paths,"Path array must not be null");
		Assert.notNull(clazz, "Class argument must not be null");
		this.configResources = new Resource[paths.length];
		for(int i = 0; i < paths.length; i++) { this.configResources[i] = new ClassPathResource(paths[i], clazz); } // Refresh (); }Copy the code

In this constructor, we only need to focus on the last method, refresh(), which is the entry point for container loading. Let’s follow through on the details.

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context forRefresh. // Pre-processing before refreshing containers, including deleting old containers such as prepareRefresh(); // Create the bean factory. // Load the parse XML file. And finish the loading and registered ConfigurableListableBeanFactory BeanDefinition the beanFactory = obtainFreshBeanFactory (); // Prepare the bean factoryfor use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
                initMessageSource();

				// Initialize event multicaster for this context.
                initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
                onRefresh();

				// Check forlistener beans and register them. registerListeners(); // Instantiate all Remaining (non-lazy-init) singletons. // Instantiate all remaining singletons (non-lazy-loading) singletons finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }... }Copy the code

There are many steps to loading a Spring container, and we focus on two of them

  • ObtainFreshBeanFactory (): This method basically creates the Spring Bean factory, loads the XML configuration file, parses the bean labels in the configuration file into BeanDefinition objects, and registers them in the Spring container
  • FinishBeanFactoryInitialization (the beanFactory) : this method is to instantiate the spring china-africa lazy loading of singleton beans, at the same time in the spring dependency injection and generate a proxy object is done in the process.

The rest of the steps are to initialize some BeanPostProcessor, including some processing of BeanDefinition, initialize some special beans, register listeners, and broadcast the loaded message. These functional branches, you can be interested in their own understanding, here is not a detailed description. Today, we will focus on the loading and initialization process of the springBean. That is, we basically see finishBeanFactoryInitialization completed by this method.

Next, we will then see what finishBeanFactoryInitialization this method completed:

Protected void finishBeanFactoryInitialization (ConfigurableListableBeanFactory the beanFactory) {... / / Instantiate all remaining (non - lazy - init) singletons. / / complete singleton Bean initializes the beanFactory. PreInstantiateSingletons (); }Copy the code

We don’t need to pay too much attention to the first part of this method. At the end of the method, this preInstantiateSingletons() is the entry to instantiate our singleton bean:

public void preInstantiateSingletons() throws BeansException {
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which inturn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... // Load all non-lazy singleton benasfor (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) { final FactoryBean<? > factory = (FactoryBean<? >) bean; boolean isEagerInit;if(System.getSecurityManager() ! = null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)  ((SmartFactoryBean<? >) factory)::isEagerInit, getAccessControlContext()); }else{ isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}}else{// Ordinary beans go to the following method getBean(beanName); }}}Copy the code

Let’s go through the method step by step

  • Facilitate all beanName, and then retrieve the corresponding BeanDefinition object from the BeanDefinition collection
  • Then determine if the object is not abstract, is a singleton, and is not lazily loaded, and execute the code below if()
  • Then determine if the bean is a special factory bean and execute the corresponding method.
  • The last step is to load the normal bean

We’ll then follow the getBean method a little further down to the doCreateBean method that actually creates the bean in AbstractBeanFactory

Here the code is very long, here we will not post out, we follow my ideas to look down on it *

Object sharedInstance = getSingleton(beanName);
Copy the code

Spring has three levels of caching. The first level of caching is where the bean objects loaded by Spring are stored. The second – and third-tier caches are designed to solve the problem of loop dependency in Spring (which is important and will be explained in detail later).

/ / to get objects from level 1 cache is the map container Object singletonObject = this. SingletonObjects. Get (beanName);if(singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // From the second level cache attempts to acquire the singleton bean singletonObject = this. EarlySingletonObjects. Get (beanName); // allowEarlyReference // Whether to allow fetching objects from level 3 cache is whether to allow cyclic dependenciesif(singletonObject == null && allowEarlyReference) {// Try to get the singleton bean ObjectFactory<? > singletonFactory = this.singletonFactories.get(beanName);if(singletonFactory ! = null) {/ / by singleton factory for singleton bean singletonObject = singletonFactory. GetObject (); / / put the beans in the second level cache enclosing earlySingletonObjects. Put (beanName singletonObject); / / will bean is removed from the l3 cache enclosing singletonFactories. Remove (beanName); }}}}return singletonObject;
Copy the code

This is the process of getting a singleton bean, where the three-level cache holds the bean’s factory object.

Let’s move on to the code below doCreateBean

  • The next step is to determine whether a bean object has been obtained. If a Factory object has been obtained, the corresponding bean instance is generated
  • If no bean object is retrieved, else is entered to create the bean object
  • Next, we’ll look at our main method of creating a singleton bean
if(mbd.issingleton ()) {sharedInstance = getSingletion(beanName, () -> {try {// The main method to create a singleton Beanreturn createBean(beanName, mbd, args);
				}
				catch (BeansException ex) {
					// Explicitly remove instance from singleton cache: It might have been put there
					// eagerly by the creation process, to allow forcircular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }Copy the code
  • This method uses the lamda expression, which you can click on directly in the getSingleton method. It’s a long method and we’re going to focus on these two lines of code
	singletonObject = singletonFactory.getObject();
	newSingleton = true;
Copy the code

The real implementation of this getObject() method is the createBean(beanName, MBD, args); This method is the way to actually create a bean. We’ll look at that in a minute. The final part of the getSingletion method is to add the created bean to the map and remove the secondary and tertiary caches

addSingleton(beanName, singletonObject);
Copy the code
protected void addSingleton(String beanName, The Object singletonObject) {synchronized (enclosing singletonObjects) {/ / added to the level of this cache. SingletonObjects. Put (beanName, singletonObject); / / delete the l3 cache this. SingletonFactories. Remove (beanName); / / delete the second level cache this. EarlySingletonObjects. Remove (beanName); / / will beanName added to the already created beanName collection of enclosing registeredSingletons. Add (beanName); }}Copy the code

Now let’s look at the last creatBean method that creates the bean

. // Complete the creation of Bean instances including - instantiation, dependency injection (populating property values), initialization (calling initialization methods) Object beanInstance =doCreateBean(beanName, mbdToUse, args);
			if (logger.isDebugEnabled()) {
				logger.debug("Finished creating instance of bean '" + beanName + "'");
			}
			returnbeanInstance; }...Copy the code

Here we focus on the doCreateBean(), and we continue to work our way down to the code that creates the bean. This code is a bit long, so let’s grab some snippets

// Instantiate the bean.... //1. Instantiate bean objectsif(instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } // Instantiated Bean object... // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces Like BeanFactoryAware. // If the object is singleton, loop dependencies are allowed, And is creating a Boolean earlySingletonExposure = (MBD isSingleton () && enclosing allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); // To resolve the loop dependency, expose the singleton Bean ahead of time and place the Bean in the level 3 cacheif (earlySingletonExposure) {
			if (logger.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, MBD, bean)); } // Initialize the bean instance. Object exposedObject = bean; Try {// Property filling is the DI procedure populateBean(beanName, MBD, instanceWrapper); ExposedObject = initializeBean(beanName, exposedObject, MBD); }...return exposedObject;
Copy the code

This can be divided into three main steps

  • Instantiate the singleton Bean and put the singleton Bean into the level 2 cache
  • Populate the bean’s properties
  • To initialize a bean is to call the initialization method to complete the initialization operation of the bean

conclusion

Now that we've looked at the main springIOC loading process, we've looked at the bean creation process and how the bean is created and added to the container. There is also the problem of circular dependency and the process of attribute filling that we have not yet understood and read. We will leave these two problems to the following article to explain to you, I hope you pay more attention to them, thank you for reading.Copy the code