1. The principle of

    • Principle of focus: Focus on the main line
    • Macro Principle: In God’s eyes, focus on the source code structure and business process, not how the code is written
  2. Read source code methods and techniques

    • Breakpoints (look at the call stack)
    • Lead Lead (Find Left)
    • Experience (doXXX in the Spring framework is where the concrete processing is done)

1. Container architecture of Spring IoC

1.1 Spring IoC’s container architecture

  • ApplicationContext is a high-level interface to a container, and BeanFactory (a hierarchical container/root container that defines/regulates the basic behavior of the container). The Spring application context, officially known as the IOC container, A container is simply a map. To be precise, a map is a member of the IOC container, called a singleton pool. A container is a collection of components and procedures, including a BeanFactory, a singleton pool, a BeanPostProcessor, and the collaboration between them.)

1.2 Key timing points in the Bean life cycle

1.3 Main process of Spring IoC Container initialization

The refresh method can also be called after the container is refreshed

2 BeanFactory Creation process

2.1 Obtaining the BeanFactory subprocess

AbstractApplicationContext.java

/**
	 * Tell the subclass to refresh the internal bean factory.
	 * @return the fresh BeanFactory instance
	 * @see #refreshBeanFactory()
	 * @see #getBeanFactory() * Does two things: Get the BeanFactory * 1, the default implementation DefaultListableBeanFactory * 2 to parse the XML bean load for BeanDefinition object, and register to BeanDefinitionRegistry (map, Key = id or name, value = BeanDefinition object) */
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

		/** * If the bean Factory exists, destroy the beans. Close the bean factory * if not instantiate DefaultListableBeanFactory, set up the serialization id, customize bean plant some attributes (whether coverage, whether to allow circular dependencies), load application beanDefinitions * /
		refreshBeanFactory();
		return getBeanFactory();
	}
Copy the code

AbstractRefreshableApplicationContext.java

protected final void refreshBeanFactory() throws BeansException {
		// Check if there is a bean Factory
		if (hasBeanFactory()) {
			/ / destruction of beans
			destroyBeans();
			// Close the bean Factory
			closeBeanFactory();
		}
		try {
			/ / instantiate DefaultListableBeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			// Set the serialization ID
			beanFactory.setSerializationId(getId());
			// Customize some attributes of the bean factory (whether to override, whether to allow cyclic dependencies)
			customizeBeanFactory(beanFactory);
			//加载应用中的beanDefinitions
			loadBeanDefinitions(beanFactory);
			this.beanFactory = beanFactory;
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code

2.2 BeanDefinition load parsing and registration subprocess

  1. This subprocess involves several key steps as follows

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory. support.DefaultListableBeanFactory) org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory. xml.XmlBeanDefinitionReader) org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String…) org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource) Org. Springframework. Beans. Factory. XML. XmlBeanDefinitionReader# doLoadBeanDefinitions reads the XML Document object to complete

Org. Springframework. Beans. Factory. XML. XmlBeanDefinitionReader# registerBeanDefinitions (registration) org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

  1. Process analysis

  2. Sequence diagram

3 Bean creation process

org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

  1. From the initial critical timing point analysis, we know that the Bean creation subprocess entry is at

FinishBeanFactoryInitialization AbstractApplicationContext# refresh () method (the beanFactory)

/** * Instantiate all Remaining (non-lazy-init) singletons. ** Instantiate all remaining (non-lazy-init) singletons * Initialize method calls (such as afterPropertiesSet, init-method) * Call BeanPostProcessor (post-processor) to post-process the instance Bean * */
				finishBeanFactoryInitialization(beanFactory);
Copy the code
  1. Enter the finishBeanFactoryInitialization
/** * Finish the initialization of this context's bean factory, * Initializing all remaining singleton beans. * Finish the initialization of the bean factory * instantiate all singleton beans */
	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		// Whether to include the bean for the conversion service (for type conversion)
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no bean post-processor
		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
		if(! beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); }// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false.false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
		beanFactory.freezeConfiguration();

		// Instantiate all remaining (non-lazy-init) singletons.
		// Instantiate all immediately loaded beans
		beanFactory.preInstantiateSingletons();
	}
Copy the code
  1. Continue to enter the DefaultListableBeanFactory class preInstantiateSingletons method, we find the code for the following part, see factories Bean or common Bean, finally by getBean method for instance
if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					// If it is a FactoryBean, add &
					if (bean instanceofFactoryBean) { 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 instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}}else {
					// Instantiate the current beangetBean(beanName); }}Copy the code
  1. Following along, we’ll go to the AbstractBeanFactory class’s doGetBean method, which has a lot of code, and we’ll go straight to the core

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

// Create a singleton bean
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							/ / create a bean
							return 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 for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }Copy the code
  1. Then enter the AbstractAutowireCapableBeanFactory class method, find the following code section

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

try {
			// Enter, actually creating the bean
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
Copy the code
  1. Take a look at the doCreateBean method, where we focus on two key areas

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

if (instanceWrapper == null) {
			// Create the bean instance, just calling the constructor, but not setting the properties
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
                
Copy the code

Populate the Bean with properties, call the initialization method, and apply the BeanPostProcessor post-processor

try {
			// Bean property population
			populateBean(beanName, mbd, instanceWrapper);
			// Call the initialization method and apply the BeanPostProcessor post-processor
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
Copy the code

4 Principle of lazy-init lazy loading mechanism

Initialization of normal beans is performed during the container-initiated initialization phase, whereas lazy-init=true beans are fired when context.getBean() is first called from the container. Spring starts by parsing all bean information (including XML and annotations) into beanDefinitions that Spring recognizes and storing them in a Hashmap for initialization. Each BeanDefinition is then processed. Lazy loads are not processed during container initialization, while others are initialized and dependency injected during container initialization.

org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

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

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		// All beanDefinitions
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		// Triggers the initialization of all non-lazy-loaded singleton beans, the main step being getBean
		for (String beanName : beanNames) {
			// Merge BeanDefinition objects
			// map.get(beanName)
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
                        // Determine if the singleton bean is lazily loaded, and if it is singleton and not lazily loaded, initialize it at container creation time
			if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {// Check if it is a FactoryBean
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					// If it is a FactoryBean, add &
					if (bean instanceofFactoryBean) { 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 instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}}else {
					// Instantiate the current bean
                                        Context.getbean ("beanName") triggers the same lazy loading logic as context.getBean("beanName")getBean(beanName); }}}// Trigger post-initialization callback for all applicable beans...
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if(System.getSecurityManager() ! =null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else{ smartSingleton.afterSingletonsInstantiated(); }}}}Copy the code
  • conclusion
    • For beans decorated as lazy-init, the Spring container does not init and dependency injection during the initialization phase, but only when the getBean is first initialized and dependency injected
    • For non-lazy-loaded beans, the getBean is fetched from the cache because the bean has already been initialized and cached during container initialization

5. Spring IoC loop dependency

There is no init and dependency injection during the Spring container initialization phase, only when the getBean is initialized for the first time

5.1 What are Circular dependencies

Circular dependencies are circular references, in which two or more beans hold each other and form a closed loop. For example, A depends on B, B depends on C, and C depends on A.

Note that this is not a circular call to a function, but an interdependency of objects. A circular call is an infinite loop unless there is a termination condition.

The cycle dependency scenarios in Spring are:

  • Constructor loop dependencies (constructor injection)
  • Circular dependencies for Field properties (set injection)

Constructor of circular dependencies problem cannot be solved, can only throw BeanCurrentlyInCreationException abnormalities, when solving attribute circular dependencies, spring is exposed object method in advance.

5.2 Cyclic dependency handling mechanism

  • Singleton bean constructor parameter loop dependency (unresolvable)
  • Prototype bean loop dependency

Spring will error handle the initialization of the prototype bean either through the constructor parameter cyclic dependency or through the setXxx method.

AbstractBeanFactory. DoGetBean () method:

 
if (isPrototypeCurrentlyInCreation(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);
}
Copy the code
 
protected boolean isPrototypeCurrentlyInCreation(String beanName) { Object curVal = this.prototypesCurrentlyInCreation.get(); return(curVal ! =null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<? >) curVal).contains(beanName)))); }Copy the code

Throw an exception if the prototype bean is being created before fetching the bean. The beanName is being created, and the tag will be removed when the creation is complete

 
try {
// Add the tag before creating the prototype bean. BeforePrototypeCreation (beanName);
// Create the prototype bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// Delete the tag after the prototype bean is created
    afterPrototypeCreation(beanName);
}
Copy the code

Bottom line :Spring does not support cyclic dependencies for prototype beans.

  • Singleton beans loop dependent via setXxx or @autoWired (resolvable)

Spring’s theory of cyclic dependency is based on Java reference passing. When a reference to an object is obtained, the object’s properties can be set later. Spring does this by exposing an ObjectFactory object ahead of time. Simply put, after ClassA calls the constructor to initialize the object, ObjectFactory exposes ClassA instantiated objects to the Spring container before calling ClassA’s setClassB method.

  • ClassA is pre-exposed to the Spring container after the object is initialized by the constructor.
 
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
AddSingletonFactory (beanName, new ObjectFactory() {addSingletonFactory(beanName, new ObjectFactory() {
@Override
        public Object getObject() throws BeansException {
          returngetEarlyBeanReference(beanName, mbd, bean); }}); }Copy the code
  • ClassA calls the setClassB method, and Spring first tries to get ClassB from the container, which does not exist in the Spring container.

  • The Spring container initializes ClassB and also exposes ClassB to the Spring container ahead of time

  • LassB calls the setClassA method, and Spring gets ClassA from the container because it was exposed in advance in step 1

ClassA, so you can get an instance of ClassA ClassA gets ClassB from the Spring container, and you initialize the object

  • This completes the object initialization of ClassA and ClassB, and resolves the loop dependency problem.