Spring Load IOC Container and Load Bean Source Interpretation

Keywords: Programming Spring SpringBoot Maven Junit

. SpringBoot application development is already very common. It's true that SpringBoot brings us a lot of convenience for development, but in fact, SpringBoot does not add any new features, it just mixes upgrades based on Spring notes.The essence of SpringBoot is automated assembly (@EnableAutoConfiguration). We don't need to introduce as many dependent packages as we used to when building projects with Maven, and we also need to deal with all kinds of package conflicts. It's just too annoying to rely on SpringBoot core packages to complete simple application development, so understanding Spring core principles is essential.Face We started to analyze Spring's underlying principles from the source point of view.
Below we first define an entity class and a configuration class

/**
 * @Description: Dog Entity Class
 * @Auther: wenqi
 * @Date: 2019/4/19 20:22
 */
@Data
@ToString
public class Dog  {

    private String name;

    private Integer age;

    public Dog(){
        System.out.println("------>constructor call");
    }

    public void init(){
        System.out.println("------>init function call");
    }
}

/**
 * @Description: Configuration Configuration Class
 * @Auther: wenqi
 * @Date: 2019/4/19 20:30
 */
@ComponentScan("com.study.spring")
@Configuration
public class DogConfiguration {

    public Dog dog(){
        return new Dog();
    }
}

Next, decide on a test class

/**
 * @Description: Test Class
 * @Auther: wenqi
 * @Date: 2019/4/19 20:31
 */
public class Test {
    @org.junit.Test
    public void test(){
        AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(DogConfiguration.class);
        Dog dog = (Dog) ac.getBean("dog");
        System.out.println(dog);
    }
}

Starting with the AnnotationConfigApplicationContext class, let's explore how Spring's IOC container loads beans from the following sources:

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
	this();
	register(annotatedClasses);
	//Focus on the method below to create a refresh container
	refresh();
	}

We then continue to trace down this refresh() method, which is under the abstract class AbstractApplicationContext

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 1. Preprocessing before refreshing
		prepareRefresh();

		// 2. Get BeanFactory
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// 3. Prepare for BeanFactory (BeanFactory has some settings)
		prepareBeanFactory(beanFactory);

		try {
			// 4. Post-processing after BeanFactory preparation is complete
			postProcessBeanFactory(beanFactory);

			// 5. Ways to execute BeanFactoryPostProcessor
			invokeBeanFactoryPostProcessors(beanFactory);

			// 6. Register BeanPostProcessor (Bean's PostProcessor), BeanPostProcessor of different interface types; Execution timing before and after Bean creation is different
			registerBeanPostProcessors(beanFactory);

			// 7. Initialize the MessageSource component (for internationalization; message binding, message resolution)
			initMessageSource();

			// 8. Initialize event dispatcher;
			initApplicationEventMulticaster();

			// 9. Leave to subcontainers (subclasses), empty methods
			onRefresh();

			// 10. Register the ApplicationListener inside all projects in the container
			registerListeners();

			// 11. Focus on this, initialize all remaining single instance beans (custom single beans)
			finishBeanFactoryInitialization(beanFactory);

			// 12. Complete the initial creation of the BeanFactory; the IOC container is created
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();

			// Reset 'active' flag.
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

Through the above steps, we find that Spring's IOC container mainly accomplishes the following:
1, Spring containers will save the definition information of all registered beans when they start up;
1) Register bean s with xml;
2) Annotate registration beans; @Service, @Component, @Bean, xxx
2, Spring containers will create these beans at the right time
1) When you use this bean; use getBean to create the bean; create it and save it in a container later;
(2) when all remaining bean s are created uniformly; finishBeanFactoryInitialization();
3, Post Processor; BeanPostProcessor
1) Each bean is created and processed with a variety of post-processors to enhance its functionality;
AutowiredAnnotationBeanPostProcessor: Handles automatic injection
AnnotationAwareAspectJAutoProxyCreator: to do AOP functions;
xxx…
Enhanced Functional Notes:
AsyncAnnotationBeanPostProcessor

4, Event Driven Model;
ApplicationListener; event monitoring;
ApplicationEventMulticaster; event dispatch:

Next, let's look at the process of loading our own defined single beans, mainly through 11, finishBeanFactoryInitialization(beanFactory); methods.

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
	// 1. Determine if there is a ConversionService(bean Property Type Conversion Service Interface) and initialize it
	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));
	}

	// 2. If the beanFactory does not contain EmbeddedValueResolver, add an EmbeddedValueResolver to it
       // EmbeddedValueResolver-->Parse placeholders and expressions in bean s
	if (!beanFactory.hasEmbeddedValueResolver()) {
		beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
			@Override
			public String resolveStringValue(String strVal) {
				return getEnvironment().resolvePlaceholders(strVal);
			}
		});
	}

	// 3. Initialize LoadTimeWeaverAware type bean s
      // LoadTimeWeaverAware-->Weave in third-party modules such as AspectJ when loading Spring Bean
	String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
	for (String weaverAwareName : weaverAwareNames) {
		getBean(weaverAwareName);
	}

	// 4. Release Temporary Class Loader
	beanFactory.setTempClassLoader(null);

	// 5. BeanDefinition metadata frozen cache
	beanFactory.freezeConfiguration();

	// 6. Focus on instantiating all remaining (non-lazy init) singletons (including custom singleton beans)
	beanFactory.preInstantiateSingletons();
}

Continue tracking down the code, 6, beanFactory.preInstantiateSingletons() is located under the class DefaultListableBeanFactory with the following source code:

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

	// 1. Get Bean Definition Information
	List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
	// 2. Trigger initialization of non-lazy load singleton beans
	for (String beanName : beanNames) {
		// 2.1. RootBeanDefinition to get beans from beanName
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
		    // 2.2. When FactoryBean is judged
			if (isFactoryBean(beanName)) {
				final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
				boolean isEagerInit;
				if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
					isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
						@Override
						public Boolean run() {
							return ((SmartFactoryBean<?>) factory).isEagerInit();
						}
					}, getAccessControlContext());
				}
				else {
					isEagerInit = (factory instanceof SmartFactoryBean &&
							((SmartFactoryBean<?>) factory).isEagerInit());
				}
				if (isEagerInit) {
				    // 2.2.1, Focus on this, Get Create bean	
					getBean(beanName);
				}
			}
			// Non FactoryBean
			else { 
				getBean(beanName);
			}
		}
	}
     // 3. After all beans are created using get Bean s, check if all beans are SmartInitializingSingleton interfaces; if so, execute afterSingletonsInstantiated();?
	for (String beanName : beanNames) {
		Object singletonInstance = getSingleton(beanName);
		if (singletonInstance instanceof SmartInitializingSingleton) {
			final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged(new PrivilegedAction<Object>() {
					@Override
					public Object run() {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}
				}, getAccessControlContext());
			}
			else {
				smartSingleton.afterSingletonsInstantiated();
			}
		}
	}
}	

Focus code is 2.2.1, getBean(beanName); based on the beanName to get the single bean information, we continue to track the code, located in the class AbstractBeanFactory

@Override
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 {

    // 1. Convert the name of the bean, remove the & prefix, and use the alias first if the bean has an alias
    final String beanName = transformedBeanName(name);
    Object bean;

    // 2. Getting bean s from the cache
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    // 3. If the bean cannot be retrieved from the cache, re-create the bean
    else {
        // Fail if we're already creating this bean instance: We're assumably within a circular reference.
        // 3.1 Determines if a specified prototype pattern bean is currently being created (within the current thread) and throws an exception if it is -> (Spring does not resolve the circular dependency of the prototype pattern bean)
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // Check if bean definition exists in this factory.
        // 3.2 Detect whether the bean definition exists in the beanFactory
        BeanFactory parentBeanFactory = getParentBeanFactory();
        // If the current BeanFactory does not contain a beanDefinition definition for a given beanName and the parent beanFactory is not empty, go to the parent beanFactory and look again
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // 3.3 Convert name to original beanName
            // 	   Because the name here has already been canonicalized by the beanName, for example: &myBean-->canonical-->myBean
            // 	   So when we go to the parent beanFactory again, we need to convert the beanName back to the original beanName, myBean-->&myBean
            String nameToLookup = originalBeanName(name);
            // 3.4 The various getBean method overloads are recursively called below to load beans from the parent factoryBean of the current bean
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
            }
            else if (args != null) {
                // Delegate parentBeanFactory to use explicit parameter tuning if parameter is not empty
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else if (requiredType != null) {
                // Empty parameter delegates parentBeanFactory to get bean s using the standard getBean method
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
            else {
                // Otherwise, delegate parentBeanFactory to use the default getBean method
                return (T) parentBeanFactory.getBean(nameToLookup);
            }
        }

        // 3.3 If the current bean is not for type checking, mark the bean as either created or about to be created
        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        try {
            // 3.4 Merge beanDefinition, traverses all its parent beans if the specified bean is a child bean
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // Check the combined beanDefinition and throw an exception if the validation fails
            checkMergedBeanDefinition(mbd, beanName, args);

            // 3.5 Ensure that the beans on which the current beans depend are initialized.
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                // Loop through all dependent bean s and instantiate them recursively
                for (String dep : dependsOn) {
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // Registration Dependency
                    registerDependentBean(dep, beanName);
                    try {
                        // Instantiate dependent bean s
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

            // 3.6 Create a single bean
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        // Create bean s
                        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);
                        throw ex;
                    }
                });
                bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }

            // Create prototype pattern bean
            else if (mbd.isPrototype()) {
                // It's a prototype -> create a new instance.
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }

            else {
                String scopeName = mbd.getScope();
                final Scope scope = this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    // Check if required type matches the type of the actual bean instance.
    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
            return convertedBean;
        }
        catch (TypeMismatchException ex) {
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T) bean;
}

This process is preparation for creating the current single bean.These include detection of BeanDefinition, detection of parent BeanFactory if BeanDefinition is not included in the current BeanFactory, consolidation of BeanDefinition, initialization of dependent beans, and so on.When you get the beans, you fetch them from the cache in the cached Map first, and if they are not in the cache, go to step 3.6 and start creating a single bean.
Due to the length of this article, we will continue with the Bean creation process in the following article.

Posted by Weiry on Mon, 29 Apr 2019 02:10:36 -0700