SpringBoot startup process of Spring

Posted by Tremour on Tue, 09 Nov 2021 22:42:23 +0100

preface

The startup process of SpringBoot is relatively long, which will involve events / listeners of other modules, and these listeners will have their own logic; This paper focuses on the startup process. The more important listeners that have an impact on the overall situation will also be picked up separately to explain its role. The article is very long. First, give the general process of startup:

Startup class

@SpringBootApplication
public class WebApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }

}

Springapplication #run (Java. Lang. class <? >, Java. Lang. string...) has two steps:

  1. Create spring application;
  2. Call the run method;

new SpringApplication()

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// Search for the 'ApplicationContextInitializer' class from spring.factories
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// Search for the 'ApplicationListener' class from spring.factories
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

run method

The run method is more important. Here is an overview, and then we will analyze it step by step.

public ConfigurableApplicationContext run(String... args) {
    // Counting stopwatch
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	// Search for the 'SpringApplicationRunListener' class from spring.factories
	// Will find ` eventpublishingrunnlistener`
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// Call the 'starting' method in 'SpringApplicationRunListener'
	// An 'ApplicationStartingEvent' event will be built in 'eventpublishing runlistener'
	// And find listeners to monitor the event and then call them.
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// Prepare environment
		// The variables in the startup command and the environment variables of the system will be placed in two propertiespropertysources
		// Bind the environment object to the current 'SpringApplication'
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		// Take 'spring.beaninfo.ignore' from the environment variable and set it to the system object
		configureIgnoreBeanInfo(environment);
		// Print banner. You can execute your own banner through the 'spring.banner.location' attribute
		Banner printedBanner = printBanner(environment);
		// Create ` ConfigurableApplicationContext`
		context = createApplicationContext();
		// Search for the 'SpringBootExceptionReporter' class from spring.factories
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		refreshContext(context);
		// In 'SpringApplication', this method is empty
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
	    // Call the 'running' method in 'SpringApplicationRunListener'
	    // An 'ApplicationReadyEvent' event will be built in 'eventpublishing runlistener'
	    // And find listeners to monitor the event and then call them.
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

The following will be analyzed in sequence with listeners.starting, prepareEnvironment, createApplicationContext, prepareContext and refreshContext as entries.

EventPublishingRunListener

In the comment of the run method, we mentioned that the current spring application runlistener only found one eventpublishingrunnlistener:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    @Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}
	
		@Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
	...
}

As can be seen from the above code, eventpublishing runlistener exists as a broadcaster, constructs events and broadcasts them to qualified listeners.

listeners.starting

listeners.starting sends an ApplicationStartingEvent event. Through the breakpoint, we can see that the following listeners listen to the event:

  • LoggingApplicationListener: get the log system. The options are: ch.qos.logback.core.Appender, org.apache.logging.log4j.core.impl.Log4jContextFactory, java.util.logging.LogManager; And execute beforeInitialize to reset the logging system to limit output
  • BackgroundPreinitializer: for some time-consuming tasks, use a background thread to trigger them as early as possible to start initialization

prepareEnvironment

First look at its method body:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
	// Create environment objects
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	// Configure the environment according to the startup parameters
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	// Add a 'ConfigurationPropertySource' to the 'environment' attachment`
	ConfigurationPropertySources.attach(environment);
	// Call the 'environmentPrepared' method in 'SpringApplicationRunListener'
    // An 'ApplicationEnvironmentPreparedEvent' event will be built in 'eventpublishing runlistener'
    // And find listeners to monitor the event and then call them.
	listeners.environmentPrepared(environment);
	// Bind the 'environment' object to the current 'SpringApplication'`
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
	    // Convert 'environment' to 'StandardServletEnvironment' type
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}
	// Add a 'ConfigurationPropertySource' to the 'environment' attachment again`
	ConfigurationPropertySources.attach(environment);
	return environment;
}

Next, we will go in one method by one for detailed analysis.

getOrCreateEnvironment

private ConfigurableEnvironment getOrCreateEnvironment() {
	if (this.environment != null) {
		return this.environment;
	}
	// The webApplicationType here is SERVLET
	switch (this.webApplicationType) {
	case SERVLET:
		return new StandardServletEnvironment();
	case REACTIVE:
		return new StandardReactiveWebEnvironment();
	default:
		return new StandardEnvironment();
	}
}

What did you do while continuing to explore the new StandardServletEnvironment():

public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
		propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
		if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
			propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
		}
		super.customizePropertySources(propertySources);
	}

	@Override
	public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
		WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
	}
}

If no constructor is found, we continue to find its parent class:

public class StandardEnvironment extends AbstractEnvironment {
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(
				new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(
				new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}
}

Still no, keep going up:

public abstract class AbstractEnvironment implements ConfigurableEnvironment {

	private final MutablePropertySources propertySources = new MutablePropertySources();

	private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);

	public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
	}
}

Found. The customizePropertySources method was called in the constructor. We again Look back Knowing that it adds two stubpropertysources, it calls again Parent class A PropertiesPropertySource and SystemEnvironmentPropertySource are added to the customerpropertysources of. These two propertysources store the variables in the startup command and the system environment variables respectively.

configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    // true in 'SpringApplication'
    // A 'ApplicationConversionService' is set to meet the type conversion and formatting requirements of most 'springboot' applications
    // 
	if (this.addConversionService) {
		ConversionService conversionService = ApplicationConversionService.getSharedInstance();
		environment.setConversionService((ConfigurableConversionService) conversionService);
	}
	// Add properties from the command line
	configurePropertySources(environment, args);
	// Add additional configuration files to the environment object
	// The existing configuration files are obtained from the 'spring.profiles.active' attribute
	// The additional configuration file is the 'additionalProfiles' value in the current' SpringApplication '
	configureProfiles(environment, args);
}

Now let's take a look at the converter s and formatter s preset by ApplicationConversionService:

registry.addConverter(new StringToDurationConverter());
registry.addConverter(new DurationToStringConverter());
registry.addConverter(new NumberToDurationConverter());
registry.addConverter(new DurationToNumberConverter());
registry.addConverter(new StringToPeriodConverter());
registry.addConverter(new PeriodToStringConverter());
registry.addConverter(new NumberToPeriodConverter());
registry.addConverter(new StringToDataSizeConverter());
registry.addConverter(new NumberToDataSizeConverter());
registry.addConverter(new StringToFileConverter());
registry.addConverter(new InputStreamSourceToByteArrayConverter());
registry.addConverterFactory(new LenientStringToEnumConverterFactory());
registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());

ConfigurationPropertySources.attach

public static void attach(Environment environment) {
	Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
	MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
	// Get the environment object named ` ATTACHED_PROPERTY_SOURCE_NAME ` PropertySource`
	// From the analysis of the 'getOrCreateEnvironment' method, we can see that this' PropertySource 'was not added at that time`
	PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
	if (attached != null && attached.getSource() != sources) {
		sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
		attached = null;
	}
	// Add to environment object ` ConfigurationPropertySourcesPropertySource`
	// Set the existing 'PropertySource' to 'ConfigurationPropertySourcesPropertySource'
	// At present, it is not known why it is designed like this
	if (attached == null) {
		sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
				new SpringConfigurationPropertySources(sources)));
	}
}

listeners.environmentPrepared

Through the breakpoint, it is found that the current spring application runlistener has only one eventpublishing runlistener, so let's directly look at the environmentPrepared method in eventpublishing runlistener.

public void environmentPrepared(ConfigurableEnvironment environment) {
	this.initialMulticaster
			.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

Create an ApplicationEnvironmentPreparedEvent and multicast:

@Override
public void multicastEvent(ApplicationEvent event) {
	multicastEvent(event, resolveDefaultEventType(event));
}

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	Executor executor = getTaskExecutor();
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}

Through the breakpoint, we know that the following built-in listeners in org.springframework.boot listened to the ApplicationEnvironmentPreparedEvent event:

  • ConfigFileApplicationListener: delegate to EnvironmentPostProcessor to configure the context environment by loading properties from well-known file locations:
    1. SystemEnvironmentPropertySourceEnvironmentPostProcessor replaces SystemEnvironmentPropertySource in environment with originawaresystemepropertysource [PS: I don't know why]
    2. Spring application jsonenvironment postprocessor will spring. Application. JSON / spring_ APPLICATION_ The JSON value is parsed in JSON mode, and the key value is put into the configuration.
    3. ConfigFileApplicationListener, load the configuration items in the configuration file through PropertiesPropertySourceLoader/YamlPropertySourceLoader, and use PropertySourcesPlaceholdersResolver to handle placeholders.
  • AnsiOutputApplicationListener: print color logs on the console
  • LoggingApplicationListener: initialize the logging system,
    1. Set logfile and log level of each package
    2. Judge whether to register the hook of Shutdown event according to the logging.register-shutdown-hook configuration.
  • Classpathlogging applicationlistener: records the classpath of the thread context classloader (TCCL) at the debug level
  • DelegatingApplicationListener: delegate the event to the event listener specified by context.listener.classes
  • FileEncodingApplicationListener: when the encoding of the file system is not as expected, the application startup will be terminated. The expected encoding is specified / obtained through spring.mandatory-file-encoding, and the file system encoding is specified / obtained through file.encoding.

bindToSpringApplication

protected void bindToSpringApplication(ConfigurableEnvironment environment) {
	try {
	    // Bind the 'environment' object to the current 'SpringApplication' object
		Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
	}
	catch (Exception ex) {
		throw new IllegalStateException("Cannot bind to SpringApplication", ex);
	}
}

ConfigurationPropertySources.attach

prepareEnvironment finally executes ConfigurationPropertySources.attach again; according to code As you can see, this execution will add attached in the previous step_ PROPERTY_ SOURCE_ Name is removed from the environment object.

Summary

In this section, we have sorted out the preparation environment in the startup process, and the flow chart is given below:

createApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {
	Class<?> contextClass = this.applicationContextClass;
	if (contextClass == null) {
		try {
			switch (this.webApplicationType) {
			case SERVLET:
			    // org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
				break;
			case REACTIVE:
			    // org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
				break;
			default:
				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
			}
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
		}
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

We use SERVLET:

public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext{
    // Created 'AnnotatedBeanDefinitionReader' and 'ClassPathBeanDefinitionScanner'`
	public AnnotationConfigServletWebServerApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
}
// AnnotationConfigServletWebServerApplicationContext
<- ServletWebServerApplicationContext
<- GenericWebApplicationContext
<- GenericApplicationContext
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
	public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
	}
}

The createApplicationContext method is very simple. It is picked up separately to see what is done in its constructor.

prepareContext prepare context

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
		SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	// Set the prepared environment object to the context
	context.setEnvironment(environment);
	// Set 'resourceloader' / ` classloader 'in the context`
	// Set 'beanNameGenerator' and 'conversionService' in context.beanFactory`
	postProcessApplicationContext(context);
	// Call the 'initialize' method of 'ApplicationContextInitializer' successively
	applyInitializers(context);
	// Publish 'ApplicationContextInitializedEvent' event to event listener
	// At present, there is no such listener in the system
	listeners.contextPrepared(context);
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	// Add boot specific singleton beans
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	// If lazy loading is met, add a ` lazyinitializationbeanfactoryprocessor`
	if (this.lazyInitialization) {
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// Load the sources
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	// Load the Bean Definition into the context
	load(context, sources.toArray(new Object[0]));
	// Publish 'ApplicationPreparedEvent' event to event listener
	listeners.contextLoaded(context);
}

context.setEnvironment() & postProcessApplicationContext

Context. Setenvironment () & the two methods of postprocessapplicationcontext are more intuitive and do not have so many twists and turns.

// context.setEnvironment
public void setEnvironment(ConfigurableEnvironment environment) {
	super.setEnvironment(environment);
	// Create a ConditionEvaluator
	// The ConditionEvaluator evaluates whether the component class annotated with @ ConditionOnXXX needs to be registered
	this.reader.setEnvironment(environment);
	this.scanner.setEnvironment(environment);
}

// postProcessApplicationContext
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    // The current 'beanNameGenerator' and 'resourceLoader' are null
	if (this.beanNameGenerator != null) {
		context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
				this.beanNameGenerator);
	}
	if (this.resourceLoader != null) {
		if (context instanceof GenericApplicationContext) {
			((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
		}
		if (context instanceof DefaultResourceLoader) {
			((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
		}
	}
	// ApplicationConversionService has been generated in the 'prepareEnvironment' stage
	// Here, get the single instance created at that time and set it in beanFactory
	if (this.addConversionService) {
		context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
	}
}

applyInitializers

protected void applyInitializers(ConfigurableApplicationContext context) {
	for (ApplicationContextInitializer initializer : getInitializers()) {
		Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
				ApplicationContextInitializer.class);
		Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
		initializer.initialize(context);
	}
}

initializers obtained in the SpringApplication constructor:

  • DelegatingApplicationContextInitializer: delegate to the initializer specified by context.listener.classes for processing
  • ContextIdApplicationContextInitializer: create a ContextId with spring.application.name as the ID, and register it to the context as a singleton
  • ConditionEvaluationReportLoggingListener: add a ConditionEvaluationReportListener listener to the context. When receiving the events ContextRefreshedEvent and ApplicationFailedEvent, record the details of condition evaluation at the level of debug
  • ConfigurationWarningsApplicationContextInitializer: warning reporting common misconfiguration
  • ServerPortInfoApplicationContextInitializer: add a listener to the context to listen for WebServerInitializedEvent events (ps: it is itself)

load

Load the bean into the application context.

// The sources here is our startup class
protected void load(ApplicationContext context, Object[] sources) {
	BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
	// `beanNameGenerator`/`resourceLoader`/`environment ` is null
	...
	loader.load();
}

protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
	return new BeanDefinitionLoader(registry, sources);
}

BeanDefinitionLoader

class BeanDefinitionLoader {
    // Note the reader/scanner class used
	BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
		this.sources = sources;
		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
		this.xmlReader = new XmlBeanDefinitionReader(registry);
		if (isGroovyPresent()) {
			this.groovyReader = new GroovyBeanDefinitionReader(registry);
		}
		this.scanner = new ClassPathBeanDefinitionScanner(registry);
		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
	}

    int load() {
    	int count = 0;
    	for (Object source : this.sources) {
    		count += load(source);
    	}
    	return count;
    }
    
    private int load(Object source) {
    	Assert.notNull(source, "Source must not be null");
    	if (source instanceof Class<?>) {
    		return load((Class<?>) source);
    	}
    	...
    }
    
    private int load(Class<?> source) {
        // Groovy
    	if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
    		...
    	}
    	// Non anonymous class, non Groove closure, parameterless constructor
    	if (isEligible(source)) {
    	    // In the 'AnnotatedBeanDefinitionReader' section, we know that the class of 'annotatedReader' is' AnnotatedBeanDefinitionReader '`
    		this.annotatedReader.register(source);
    		return 1;
    	}
    	return 0;
    }
    
    private boolean isEligible(Class<?> type) {
    	return !(type.isAnonymousClass() || isGroovyClosure(type) || hasNoConstructors(type));
    }
    ...
}

AnnotatedBeanDefinitionReader

public class AnnotatedBeanDefinitionReader {
    // The componentClasses here have only one startup class for the current project
	public void register(Class<?>... componentClasses) {
		for (Class<?> componentClass : componentClasses) {
			registerBean(componentClass);
		}
	}
	
	public void registerBean(Class<?> beanClass) {
		doRegisterBean(beanClass, null, null, null, null);
	}
	
	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) {

		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		// Judge whether to load through the ` @ ConditionXXX 'annotation
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(supplier);
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
        // Set the corresponding abd attribute according to the 'lazy' / ` primary '/ ` dependson' / ` role '/ ` description' annotation on the class
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			...
		}
		if (customizers != null) {
			...
		}
        // Encapsulate the BeanDefinition of the startup class into BeanDefinitionHolder and register it in 'registry' [actually context]
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}
}

listeners.contextLoaded

As mentioned above, in the current spring application, there is only one element in listeners - EventPublishingRunListener. The contextLoaded method in this class synchronizes the ApplicationListener array in the spring application object to the context first, and then broadcasts the ApplicationPreparedEvent event event:

public void contextLoaded(ConfigurableApplicationContext context) {
	for (ApplicationListener<?> listener : this.application.getListeners()) {
		if (listener instanceof ApplicationContextAware) {
			((ApplicationContextAware) listener).setApplicationContext(context);
		}
		context.addApplicationListener(listener);
	}
	this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}

Through the breakpoint, we know that the following built-in listeners in org.springframework.boot listen to the ApplicationPreparedEvent event event:

  • ConfigFileApplicationListener: add PropertySourceOrderingPostProcessor; The default property source (named "defaultProperties") in the Environment object is given the lowest priority
  • LoggingApplicationListener: register the prepared log related objects as singletons
  • DelegatingApplicationListener: delegate the event to the event listener specified by context.listener.classes

Summary

In this section, we have sorted out the preparation context in the startup process, and the flow chart is given below:

refreshContext refreshes the application context

Call chain tracing is performed on refreshContext. Its real face is org.springframework.context.support.AbstractApplicationContext#refresh

public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Ready to refresh
		// Some data cleaning and verification will be carried out
		prepareRefresh();

		// Get beanFactory object
		// In the current context, set the serializationId of beanFactory and return
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);

		try {
			// Post process beanFactory in subclass
			postProcessBeanFactory(beanFactory);

			// Execute beanFactory postprocessor registered as bean
			invokeBeanFactoryPostProcessors(beanFactory);

			// Instantiate and register all beanpostprocessor beans
			registerBeanPostProcessors(beanFactory);

			// Initialize MessageSource
			// MessageSource is a policy interface for parsing messages and supports parameterization and internationalization of such messages.
			initMessageSource();

			// Initialize applicationeventmulticast.
			//If not defined in the context, use simpleapplicationeventmulticast.
			initApplicationEventMulticaster();

			// Initialize a specific bean
			onRefresh();

			// Add a bean that implements ApplicationListener
			registerListeners();

			// Instantiate all remaining (non lazy init) singleton objects
			finishBeanFactoryInitialization(beanFactory);

			// Complete the refresh of this context
			// Call the onRefresh() method of LifecycleProcessor and publish the ContextRefreshedEvent event
			finishRefresh();
		}

		catch (BeansException ex) {
			...
		}

		finally {
			// Reset Spring's public reflection metadata cache, especially ReflectionUtils, AnnotationUtils, ResolvableType and CachedIntrospectionResults caches. 
			resetCommonCaches();
		}
	}
}

The following will analyze prepareRefresh, prepareBeanFactory, postProcessBeanFactory, invokebeanfactory postprocessors, registerBeanPostProcessors, initMessageSource, initapplicationeventmulticast, onRefresh, registerListeners, finishBeanFactoryInitialization and finishRefresh in turn. The simpler methods will be put together.

prepareRefresh prepare before refresh

protected void prepareRefresh() {
	this.startupDate = System.currentTimeMillis();
	this.closed.set(false);
	this.active.set(true);
    ...
	// Initializes any placeholder property source in the context
	// Remember the two 'StubPropertySource' added when creating the Environment[StandardServletEnvironment] object in the 'prepare environment' section
	// This step is to replace the two propertysources
	// However, there is no replacement because both 'servletContext' and 'servletConfig' of the current context are null
	initPropertySources();

	// Verify that all attributes marked required are resolvable 
	// No properties are currently marked as required
	getEnvironment().validateRequiredProperties();

	// Copy the 'applicationlisters' element to' earlyApplicationListeners'`
	if (this.earlyApplicationListeners == null) {
		this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
	}
	else {
		this.applicationListeners.clear();
		this.applicationListeners.addAll(this.earlyApplicationListeners);
	}
    // Initialize the 'earlyApplicationEvents' property
	this.earlyApplicationEvents = new LinkedHashSet<>();
}

obtainFreshBeanFactory & prepareBeanFactory

These two methods are relatively simple:

  • obtainFreshBeanFactory specifies the serializationId in beanFactory in context and returns it;
  • prepareBeanFactory: configure the properties of beanFactory, such as beanClassLoader, beanPostProcessor, beanExpressionResolver, and set the interface that is not automatically assembled and use the specified value to automatically assemble the specified type:
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    	// Tell the internal bean factory to use the context's class loader etc.
    	beanFactory.setBeanClassLoader(getClassLoader());
    	beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
    	// Configure the bean factory with context callbacks.
    	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    	beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    	beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    	beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    	beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    
    	// BeanFactory interface not registered as resolvable type in a plain factory.
    	// MessageSource registered (and found for autowiring) as a bean.
    	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    	beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
    	// Register early post-processor for detecting inner beans as ApplicationListeners.
    	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    
    	...
    }
    

postProcessBeanFactory

After the standard initialization of the application context, modify its internal beanFactory:

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Execute parent class ` postProcessBeanFactory`
    // The parent class here is' servletwebserver ApplicationContext '`
	super.postProcessBeanFactory(beanFactory);
	// 'basePackages' is not specified in our startup class`
	// Don't execute ` scan here`
	if (this.basePackages != null && this.basePackages.length > 0) {
		this.scanner.scan(this.basePackages);
	}
	// `annotatedClasses ` is empty, so 'register' is not executed here`
	if (!this.annotatedClasses.isEmpty()) {
		this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
	}
}

ServletWebServerApplicationContext#postProcessBeanFactory

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Add a ` BeanPostProcessor`
	beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
	// Set the dependency of 'ServletContextAware' not to be automatically assembled
	beanFactory.ignoreDependencyInterface(ServletContextAware.class);
	// Register the scope of the Web application
	// The scope of web application is: ` request '/ ` session' / ` application`
	registerWebApplicationScopes();
}

Invokebeanfactoryprocessors executes post processors

Instantiate and call all registered beanfactoryprocessor:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // Two post processes are performed here
    // 1. postProcessBeanDefinitionRegistry
    // 2. postProcessBeanFactory
    // `Getbeanfactoryprocessors ` will get three ` beanfactoryprocessors'`
    // - SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor
    // - ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor
    // - ConfigFileApplicationListener.PropertySourceOrderingPostProcessor
	PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

	// Set 'beanFactory' property
	if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}
}

Let's take a detailed look at the postprocessorregistrationdelegate.invokebeanfactoryprocessors method, which is very long:

public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

	Set<String> processedBeans = new HashSet<>();
    // The 'beanFactory' used here is' DefaultListableBeanFactory ', which belongs to the' BeanDefinitionRegistry '`
	if (beanFactory instanceof BeanDefinitionRegistry) {
		BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
		List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
		List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
        // Among the three post processors mentioned above, 'cacheingmetadatareaderfactorypost processor' and 'configurationwarningspost processor' belong to 'BeanDefinitionRegistryPostProcessor'`
		for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
			if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
				BeanDefinitionRegistryPostProcessor registryProcessor =
						(BeanDefinitionRegistryPostProcessor) postProcessor;
				// ConfigurationWarningsPostProcessor checked the package set in @ ComponentScan in this method [cannot be in 'org'/'org.springframework']
				// A 'sharedmetadatareaderfactory bean' named 'org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory' has been added to the cachingmetadatareaderfactory postprocessor`
				// And set the 'metadataReaderFactory' property of 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor' [as a 'RuntimeBeanReference']
				registryProcessor.postProcessBeanDefinitionRegistry(registry);
				registryProcessors.add(registryProcessor);
			}
			else {
				regularPostProcessors.add(postProcessor);
			}
		}
		
		List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

		// Call the 'postProcessBeanDefinitionRegistry' method of the 'BeanDefinitionRegistryPostProcessor' that implements' PriorityOrdered '
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
		currentRegistryProcessors.clear();

		// Call the 'postProcessBeanDefinitionRegistry' method of the 'BeanDefinitionRegistryPostProcessor' that implements the 'Ordered' [repeat the above without adding currentRegistryProcessors]
		postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
		for (String ppName : postProcessorNames) {
			if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
		currentRegistryProcessors.clear();

		//Execute other unexecuted ` BeanDefinitionRegistryPostProcessor`
		// Why does' postProcessorNames' need to be retrieved multiple times?
		// `getBeanNamesForType ` sets the cache, and BeanDefinition may be added when executing each 'beanDefinitionRegistryPostProcessors' method
		BeanDefinitionRegistryPostProcessors until no further ones appear.
		boolean reiterate = true;
		while (reiterate) {
			reiterate = false;
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				if (!processedBeans.contains(ppName)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
					reiterate = true;
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();
		}

		// Execute the 'beanfactoryprocessors' method of each post processor
		invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
	}

	else {
		invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
	}

	// If there is a missing 'beanfactory postprocessor', call the 'postProcessBeanFactory' method in order according to whether the PriorityOrdered/Ordered interface is implemented
	// The executed post processor will be skipped according to 'processedBeans'
	String[] postProcessorNames =
			beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

	List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
	List<String> orderedPostProcessorNames = new ArrayList<>();
	List<String> nonOrderedPostProcessorNames = new ArrayList<>();
	for (String ppName : postProcessorNames) {
		if (processedBeans.contains(ppName)) {
			// skip - already processed in first phase above
		}
		else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
			priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
		}
		else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
			orderedPostProcessorNames.add(ppName);
		}
		else {
			nonOrderedPostProcessorNames.add(ppName);
		}
	}
	sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
	invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

	List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
	for (String postProcessorName : orderedPostProcessorNames) {
		orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
	}
	sortPostProcessors(orderedPostProcessors, beanFactory);
	invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

	List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
	for (String postProcessorName : nonOrderedPostProcessorNames) {
		nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
	}
	invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
    // Clear cache
	beanFactory.clearMetadataCache();
}

BeanDefinitionRegistryPostProcessor

The BeanDefinitionRegistryPostProcessor used and their specific functions are listed below:

Only the BeanDefinitionRegistryPostProcessor provided by springboot is considered, and the BeanDefinitionRegistryPostProcessor introduced by other components is not considered

  • ConfigurationClassPostProcessor: the postprocessor will first load the class with @ Configuration annotation in the current beanFactory; Then traverse the found classes, parse the @ Component/@PropertySource/@ComponentScan annotation on them in order, and recursively parse the @ Component scanned by @ ComponentScan; Then load EnableAutoConfiguration in spring.factories under jar package in libraries; Finally, parse the method with @ Bean in the class, the xml file introduced by the @ ImportResource annotation on the class, and the ImportBeanDefinitionRegistrar imported by @ Import. Our startup class is one of the @ Configuration classes. The @ SpringBootApplication annotation on the startup class is a composite annotation, including @ ComponentScan. Therefore, in this step, we load various beandefinitions of the application layer into beanFactory;

BeanFactoryPostProcessor

The beanfactoryprocessor used and their specific functions are listed below:

Only the beanfactoryprocessor provided by springboot is considered, and the beanfactoryprocessor introduced by other components is not considered

  • ConfigurationClassPostProcessor: use ConfigurationClassEnhancer to enhance the Configuration class to support the bean scope defined by @ Bean, and add an ImportAwareBeanPostProcessor post processor;
  • ConfigFileApplicationListener: put the default configuration file at the end of the PropertySource list;
  • PropertySourcesPlaceholderConfigurer: create a StringValueResolver object to process the ${...} placeholder in the bean alias in the container, and then add the StringValueResolver object to the embeddedValueResolvers list of beanFactory. Subsequently, process the ${...} placeholder in @ Value, configuration and annotation;
  • EventListener methodprocessor: this class is used to encapsulate the @ EventListener method into independent applicationlister instances; In the postProcessBeanFactory method, instantiate the bean of EventListenerFactory first;
  • PreserveErrorControllerTargetClassPostProcessor: ensure that its target class is retained when using AOP by setting preserveTargetClass to true for the beanDefinition of ErrorController

registerBeanPostProcessors

Instantiate the BeanPostProcessor class in the container and add it to beanPostProcessors of beanFactory in order. Let's take a look at the functions of these system preset beanPostProcessors:

  • ApplicationContextAwareProcessor: if a bean implements some Aware interfaces, call its corresponding setting method during bean initialization;
  • WebApplicationContextServletContextAwareProcessor: pass servletContext/servletConfig to ServletContextAware/ServletConfigAware objects respectively;
  • ImportAwareBeanPostProcessor: set importMetadata of ImportAware
  • BeanPostProcessorChecker: when a bean is created during BeanPostProcessor instantiation, it is recorded through this processor;
  • ConfigurationPropertiesBindingPostProcessor: resolve the annotation @ ConfigurationProperties on the bean and set the properties in the property source to the bean.
  • AnnotationAwareAspectJAutoProxyCreator: AOP portal. For details, see previous articles: AOP source code of Spring
  • DataSourceInitializerPostProcessor: force initialization of DataSourceInitializer when DataSource is initialized.
  • AsyncAnnotationBeanPostProcessor: add AsyncAnnotationAdvisor enhancement to the class or method with @ Async annotation for asynchronous execution;
  • Rabbitlistener annotationbeanpostprocessor: add a rabbitlistener endpoint instance by parsing the @ RabbitListener/@RabbitHandler annotation on the bean or bean method;
  • MethodValidationPostProcessor: add a MethodValidationInterceptor enhancement to qualified bean s to verify @ Validated classes, methods and properties;
  • PersistenceExceptionTranslationPostProcessor: add a PersistenceExceptionTranslationAdvisor for qualified bean s to convert persistent exception information;
  • WebServerFactoryCustomizer beanpostprocessor: apply the loaded webserverfactory bean to WebServerFactoryCustomizer;
  • Errorpageregisterrbeanpostprocessor: apply the loaded ErrorPageRegistrybean to ErrorPageRegistry;
  • Projectingargumentresolverbanpostprocessor: add ProxyingHandlerMethodArgumentResolver to the first in the ArgumentResolvers list of the loaded requestmappinghandleradapter bean;
  • ConfigurationPropertiesBeans: collect beans with @ ConfigurationProperties;
  • ApplicationListenerDetector: ApplicationListener detector. If the loaded bean is ApplicationListener, it will be added to the applicationListeners list of context;

initMessageSource & initApplicationEventMulticaster

  1. initMessageSource: initializes the MessageSource, which is used to process internationalization scenarios;
  2. Initapplicationeventmulticast: create a simpleapplicationeventmulticast and add it to beanFactory;

onRefresh

protected void onRefresh() {
    // Initialize theme function
    // It's no longer deep here
	super.onRefresh();
	try {
	    // Create web Service
		createWebServer();
	}
	catch (Throwable ex) {
		throw new ApplicationContextException("Unable to start web server", ex);
	}
}

createWebServer

private void createWebServer() {
	WebServer webServer = this.webServer;
	ServletContext servletContext = getServletContext();
	// webServer was null at first
	if (webServer == null && servletContext == null) {
	    // This article uses' TomcatServletWebServerFactory '`
		ServletWebServerFactory factory = getWebServerFactory();
		// Create WebServer
		// In fact, the tomcat server has been started at this step, but our application is not fully ready to provide services
		// The process here is relatively long, and a separate article will be opened for analysis
		this.webServer = factory.getWebServer(getSelfInitializer());
		// Register two singletons in beanFactory: webServerGracefulShutdown and webServerStartStop
		// Both implement the 'SmartLifecycle' interface
		// WebServer will be started in the start callback of 'WebServerStartStopLifecycle'
		getBeanFactory().registerSingleton("webServerGracefulShutdown",
				new WebServerGracefulShutdownLifecycle(this.webServer));
		getBeanFactory().registerSingleton("webServerStartStop",
				new WebServerStartStopLifecycle(this, this.webServer));
	}
	else if (servletContext != null) {
		try {
			getSelfInitializer().onStartup(servletContext);
		}
		catch (ServletException ex) {
			throw new ApplicationContextException("Cannot initialize servlet context", ex);
		}
	}
	// This method has also appeared before, but both 'servletContext' and 'servletConfig' were null at that time
	// Here, 'servletContext' is set to 'ApplicationContextFacade' through the 'getWebServer' method`
	// Replace previous placeholder 'propertysource': servletcontextinitparams
	initPropertySources();
}

registerListeners register ApplicationListener

Add a bean that implements ApplicationListener as a listener:

protected void registerListeners() {
	// Load the previously loaded 'applicationlister' [the sources are read from 'spring.factories' or added in the event listener]
	// Add to 'applicationeventmulticast'
	// `Applicationeventmulticast ` is created on the side of ` initapplicationeventmulticast ` ` simpleapplicationeventmulticast '`
	for (ApplicationListener<?> listener : getApplicationListeners()) {
		getApplicationEventMulticaster().addApplicationListener(listener);
	}

	// Find the beanName that implements the 'ApplicationListener' interface from the BeanDefinition of the container
	// Note that this is just a beanName, not a bean
	// When necessary later, instantiate the real bean
	String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
	for (String listenerBeanName : listenerBeanNames) {
		getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
	}

	// If there are early events, broadcast them here first
	Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
	this.earlyApplicationEvents = null;
	if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
		for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
			getApplicationEventMulticaster().multicastEvent(earlyEvent);
		}
	}
}

finishBeanFactoryInitialization completes the initialization of non lazy init beans

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
	// Set the bean whose beanName is' conversionService 'to the' conversionService 'attribute of beanFactory
	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));
	}

	// In the resolution of the 'invokebeanfactoryprocessors' method
	// We mentioned the 'PropertySourcesPlaceholderConfigurer' beanfactoryprocessor`
	// A 'StringValueResolver' will be added to the 'embeddedValueResolver' list of beanFactory
	// Therefore, there is no need to generate a 'StringValueResolver' here
	if (!beanFactory.hasEmbeddedValueResolver()) {
		beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
	}

	// Preload ` LoadTimeWeaverAware`
	String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
	for (String weaverAwareName : weaverAwareNames) {
		getBean(weaverAwareName);
	}

	beanFactory.setTempClassLoader(null);

	// Freeze all bean definition s and expect no further changes
	// The freezing here is to set the 'configurationFrozen' of beanFactory to true
	// Then copy all beanDefinitionNames to the 'frozenBeanDefinitionNames' attribute of beanFactory
	beanFactory.freezeConfiguration();

	// Instantiate the remaining uninitialized non lazy loaded singleton bean s
	// After instantiation, call back the 'afterSingletonsInstantiated' method of the bean that implements the 'smartinitializingsingsingleton' interface
	// How to instantiate is not expanded here
	beanFactory.preInstantiateSingletons();
}

finishRefresh

Complete the context refresh, call the onRefresh() method of lifecycle processor and publish the contextrefreshevent event event.

protected void finishRefresh() {
	// Clean up resource cache
	clearResourceCaches();

	// Initialize ` lifecycle processor`
	// That is, if there is an instance of 'lifecycle processor' or bean definition in the current beanFactory, instantiate it;
	// If it does not exist, create a 'defaultlifecycle processor' object
	// Put the finally obtained object into the 'lifecycle processor' attribute of beanFactory
	initLifecycleProcessor();

	// Call the onRefresh method of 'lifecycle processor'
	// In 'defaultlifecycle processor', this method obtains' Lifecycle 'type bean s from the container
	// And call their 'start' methods in turn
	// Among them is the start of webServer
	getLifecycleProcessor().onRefresh();

	// Publish 'ContextRefreshedEvent' event
	// A listener related to web services is' ResourceUrlProvider ', which initializes the access path of static resources in the' ContextRefreshedEvent 'event
	publishEvent(new ContextRefreshedEvent(this));

	// Put the current context into 'MBean' (MBean if available)
	LiveBeansView.registerApplicationContext(this);
}

Summary

refreshContext can be said to be the focus of the startup process, involving web service startup, IOC, etc; The process described above is also very long. Here is a flow chart to recall and summarize:

Finishing

The following operations are more intuitive:

  1. Call the SpringApplicationRunListener``started method. The only listener of this type in the current context object is EventPublishingRunListener; Eventpublishing runlistener will construct an ApplicationStartedEvent event and broadcast it; Publish the application state change event LivenessState.CORRECT;
  2. Get the ApplicationRunner and CommandLineRunner in the container and call them in turn;
  3. Call the SpringApplicationRunListener``running method. The only listener of this type in the current context object is eventpublishingrunnlistener; Eventpublishing runlistener will construct an ApplicationReadyEvent event and broadcast it; Publish and apply state change event ReadinessState.ACCEPTING_TRAFFIC;

Topics: Java Spring Spring Boot