1 SpringApplication

The role of the SpringApplication is to launch the Spring application. They usually do a few things:

  • Create an appropriate ApplicationContext;

  • Registered CommandLinePropertySource, exposed the program startup command line parameters, as Spring environment variables or used in bean initialization, etc.;

  • Refresh the ApplicationContext and load all singleton beans;

  • The CommandLineRunner interface is executed before the program is run.

/** SpringApplication.java **/
public static ConfigurableApplicationContext run(Class
       [] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

public SpringApplication(ResourceLoader resourceLoader, Class
       ... primarySources) {
		this.resourceLoader = resourceLoader;  / / 1.1
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();  / / 1.2
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));  / / 1.3
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));  / / 1.4
		this.mainApplicationClass = deduceMainApplicationClass();  / / 1.5
}
Copy the code

1.1 ResourceLoader

Load resources like classpath: from configuration classes, from files, from urls, etc. Null here.

1.2 WebApplicationType

Web type: NONE, SERVLET, and REACTIVE.

static WebApplicationType deduceFromClasspath(a) {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if(! ClassUtils.isPresent(className,null)) {
				returnWebApplicationType.NONE; }}return WebApplicationType.SERVLET;
	}
Copy the code

If rely on existing in the package org. Springframework. Web. Reactive. DispatcherHandler classes and there is no org. Springframework. Web. Servlet. DispatcherServlet, So it’s REACTIVE.

1.3 ApplicationContextInitializer

Initialize ApplicationContextInitializer collection. From jars in the meta-inf/spring. Factories file for org. Springframework. Context. ApplicationContextInitializer value, and through reflection instance is created.

private <T> Collection<T> getSpringFactoriesInstances(Class
       
         type, Class
        [] parameterTypes, Object... args)
        {
		ClassLoader classLoader = getClassLoader();
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));  // type = ApplicationContextInitializer.class
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
Copy the code

Is the main function of ApplicationContextInitializer before ConfigurableApplicationContext type of ApplicationContext calls to refresh, Allow us to ConfiurableApplicationContext examples further set up and processing. Such as ServerPortInfoApplicationContextInitializer: set a callback function, local events are received. The server port set the environment variable.

public class ServerPortInfoApplicationContextInitializer implements
   	ApplicationContextInitializer<ConfigurableApplicationContext>, ApplicationListener<WebServerInitializedEvent> {

   @Override
   public void initialize(ConfigurableApplicationContext applicationContext) {
   	applicationContext.addApplicationListener(this);
   }

   @Override
   public void onApplicationEvent(WebServerInitializedEvent event) {
   	String propertyName = "local." + getName(event.getApplicationContext()) + ".port"; setPortProperty(event.getApplicationContext(), propertyName, event.getWebServer().getPort()); }}Copy the code

1.4 ApplicationListener

Initialize the ApplicationListener collection. From jars in the meta-inf/spring. Factories file for org. Springframework. Context. The ApplicationListener value, and through reflection instance is created.

1.5 Finding the startup class

2 run(String… args)

/** SpringApplication.java **/
public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();  / / 2.1
		stopWatch.start();  / / 2.1
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);  / / 2.2
		listeners.starting();  / / 2.2
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);  / / 2.3
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);  / / 2.4
			context = createApplicationContext();  / / 2.5
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);  / / 2.6
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);  / / 2.7
			refreshContext(context);  / / 2.8
			afterRefresh(context, applicationArguments);  / / 2.9
			stopWatch.stop();  / / 2.1
			if (this.logStartupInfo) {  / / 2.1
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);  // 2.10 Releasing the 'ApplicationStartedEvent' event
			callRunners(context, applicationArguments);  / / 2.11
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);  // 2.12 Publishing the 'ApplicationReadyEvent' event
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
Copy the code

2.1 StopWatch

For timing, here is used to calculate the program start time, in the log will find a record like this:

Started Application in 1.831 seconds (JVM running for 2.433)

2.2 SpringApplicationRunListener

From jars in the meta-inf/spring. Factories file for org. Springframework. Boot. SpringApplicationRunListener value, and through reflection instance is created, The ApplicationStartingEvent event is created from the input parameter args and notified to the listener.

Look at the part of the EventPublishingRunListener source code:

/** EventPublishingRunListener **/
public class EventPublishingRunListener implements SpringApplicationRunListener.Ordered {

	private final SpringApplication application;

	private final String[] args;
	
	private final SimpleApplicationEventMulticaster initialMulticaster;
	
	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for(ApplicationListener<? > listener : application.getListeners()) {/ / 1
			this.initialMulticaster.addApplicationListener(listener); }}@Override
	public void starting(a) {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));  / / 2}}/** SimpleApplicationEventMulticaster **/
public void multicastEvent(ApplicationEvent event) {
  multicastEvent(event, resolveDefaultEventType(event));
}

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)) {/ / 3
    if(executor ! =null) {
      executor.execute(() -> invokeListener(listener, event));  / / 4
    }
    else{ invokeListener(listener, event); }}}Copy the code

3, SimpleApplicationEventMulticaster class for all of the ApplicationListener, so when get to the event, you can inform corresponding to the listener.

4, according to type, notify corresponding listener, and call listener callback function.

2.3 ConfigurableEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    // Create and configure the environment
    ConfigurableEnvironment environment = getOrCreateEnvironment(); / / 1
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);  / / 2
    listeners.environmentPrepared(environment);  / / 3
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
       environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
             deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

public class StandardEnvironment extends AbstractEnvironment {

	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {  / / 1
		propertySources.addLast(
				new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(
				newSystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); }}public static void attach(Environment environment) {  / / 2Assert.isInstanceOf(ConfigurableEnvironment.class, environment); MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources(); PropertySource<? > attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);if(attached ! =null&& attached.getSource() ! = sources) { sources.remove(ATTACHED_PROPERTY_SOURCE_NAME); attached =null;
		}
		if (attached == null) {
			sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
					new SpringConfigurationPropertySources(sources)));  / / 2}}Copy the code

1. Create the StandardEnvironment object with the MutablePropertySources propertySources field to hold the PropertySource list. Here we get the systemEnvironment and systemProperties variables.

The variable associated with configurationProperties is retrieved and inserted at the top of the MutablePropertySources list.

3, publish ApplicationEnvironmentPreparedEvent event and notification listener callback function. ConfigFileApplicationListener, for example, the variables in the configuration file is added to the applicationConfig.

Note: The order of the elements in the MutablePropertySources list is important. If multiple sources have the same key, the first Source in the list with that key is selected. For example, ‘systemEnvironment’ has a higher priority than applicationConfig, application. Yml has server.port=8080, Jar –server.port=9090 ‘; So the actual listening port is 9090.

Look at the source of the PropertySourcesPropertyResolver:

protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
		if (this.propertySources ! =null) {
                        // If there are more than one PropertySource, traverse it one by one
			for(PropertySource<? > propertySource :this.propertySources) {
				Object value = propertySource.getProperty(key);
                                // Once the key is found, return directly
				if(value ! =null) {
					if (resolveNestedPlaceholders && value instanceof String) {
						value = resolveNestedPlaceholders((String) value);
					}
					logKeyFound(key, propertySource, value);
					returnconvertValueIfNecessary(value, targetValueType); }}}return null;
	}
Copy the code

For more information, see: Implementing decentralized configuration based on SpringBoot Environment source code understanding

2.4 the Banner

Can be customized, do not expand.

2.5 ConfigurableApplicationContext

Depending on the type of webApplicationType create ConfigurableApplicationContext instance. If it is REACTIVE type, then create AnnotationConfigReactiveWebServerApplicationContext instance.

protected ConfigurableApplicationContext createApplicationContext(a) { Class<? > contextClass =this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					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);
	}
Copy the code

Look at the AnnotationConfigReactiveWebServerApplicationContext constructor,

public AnnotationConfigReactiveWebServerApplicationContext(a) {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
Copy the code

Which AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner registered bean is used to load.

2.6 SpringBootExceptionReporter

Create SpringBootExceptionReporter instance, which is used to analyze failures and provide diagnostic information. FailureAnalyzers

FailureAnalyzers(ConfigurableApplicationContext context) {
		this(context, null);  / / 1
	}

	FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
		Assert.notNull(context, "Context must not be null");
		this.classLoader = (classLoader ! =null)? classLoader : context.getClassLoader();this.analyzers = loadFailureAnalyzers(this.classLoader);  / / 2
		prepareFailureAnalyzers(this.analyzers, context);
	}

	private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {
		List<String> analyzerNames = SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, classLoader);  // 3 Obtain from 'meta-INF /spring.factories' the implementation class of' Fail Analyzer 'that can be used to analyze program failures and provide diagnostic information, Such as ` NoUniqueBeanDefinitionFailureAnalyzer `.
		List<FailureAnalyzer> analyzers = new ArrayList<>();
		for (String analyzerName : analyzerNames) {
			try{ Constructor<? > constructor = ClassUtils.forName(analyzerName, classLoader).getDeclaredConstructor(); ReflectionUtils.makeAccessible(constructor); analyzers.add((FailureAnalyzer) constructor.newInstance());// 4 Create an instance.
			}
			catch (Throwable ex) {
				logger.trace(LogMessage.format("Failed to load %s", analyzerName), ex);
			}
		}
		AnnotationAwareOrderComparator.sort(analyzers);
		return analyzers;
	}
Copy the code

2.7 prepareContext

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);  // 1 Set environment variables
		postProcessApplicationContext(context);  / / 2
		applyInitializers(context);  / / 3
		listeners.contextPrepared(context);  / / 4
		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);
		}
    // Lazily instantiate the bean
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		Set<Object> sources = getAllSources();  / / 5
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));  / / 5
		listeners.contextLoaded(context);  / / 6
}
Copy the code

3. Execute the Initialize method in 1.3 Instance.

4, publish ApplicationContextInitializedEvent event, inform the corresponding to the listener.

5. Load the bean. The process is as follows: Get resources (such as configuration classes, XML, urls, etc.); The parse resource is BeanDefinition; Registration is done via BeanDefinitionRegistry’s registerBeanDefinition(String beanName, BeanDefinition BeanDefinition) method. ApplicationContext has a Map

beanDefinitionMap where key is beanName and value is BeanDefinition. It is in this map that new elements are added, and then bean instances can be created based on BeanDefinition.
,>

private int load(Object source) {
		Assert.notNull(source, "Source must not be null");
		if (source instanceofClass<? >) {returnload((Class<? >) source); }if (source instanceof Resource) {
			return load((Resource) source);
		}
		if (source instanceof Package) {
			return load((Package) source);
		}
		if (source instanceof CharSequence) {
			return load((CharSequence) source);
		}
		throw new IllegalArgumentException("Invalid source type " + source.getClass());
	}
Copy the code

6. Publish the ApplicationPreparedEvent event, notifying the corresponding listener to process it.

2.8 refreshContext

public void refresh(a) throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();  / / 1

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
                        // Set BeanExpressionResolver, ResourceEditorRegistrar etc
			prepareBeanFactory(beanFactory);  / / 2

			try {
				// Allows post-processing of the bean factory in context subclasses.
                                // Allow bean Factory to be modified after instantiation
				postProcessBeanFactory(beanFactory);  / / 3

				// Invoke factory processors registered as beans in the context.
                                // Instantiate and invoke all registered BeanFactoryPostProcessor beans
				invokeBeanFactoryPostProcessors(beanFactory);  / / 4

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

				// Initialize message source for this context.
                                // MessageSource: for internationalization of information
				initMessageSource();  / / 6

				// Initialize event multicaster for this context.
                                / / initialize ApplicationEventMulticaster, used to broadcast events
				initApplicationEventMulticaster();  / / 7

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

				// Check for listener beans and register them.
                                / / add listeners to applicationEventMulticaster (see step7), is used to publish events;
                                // If an existing event (earlyApplicationEvents! = null), publish directly
				registerListeners();  / / 9

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);  / / 10

				// Last step: publish corresponding event.
				finishRefresh();  / / 11
			}

			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(); }}}Copy the code

1, prepareRefresh () : sets the current Context to the active state, check the required environment variables, initialize earlyApplicationListeners and earlyApplicationEvents.

protected void prepareRefresh(a) {
		// Switch to active.
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);

		// Initialize any placeholder property sources in the context environment.
		initPropertySources();

		/ / by ConfigurablePropertyResolver# setRequiredProperties Settings required environment variables;
		// Then check that the required variables are present
		getEnvironment().validateRequiredProperties();

		// Store pre-refresh ApplicationListeners...
		if (this.earlyApplicationListeners == null) {
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			// Reset local application listeners to pre-refresh state.
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}

		// Allow for the collection of early ApplicationEvents,
		// to be published once the multicaster is available...
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}
Copy the code

OnRefresh () : Initializes some special beans. ReactiveWebServerApplicationContext, for example, to create a ReactiveWebServerFactory, WebServer object, etc.

protected void onRefresh(a) {
		super.onRefresh();
		try {
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start reactive web server", ex); }}private void createWebServer(a) {
		ServerManager serverManager = this.serverManager;
		if (serverManager == null) {
			String webServerFactoryBeanName = getWebServerFactoryBeanName();
			ReactiveWebServerFactory webServerFactory = getWebServerFactory(webServerFactoryBeanName);
			boolean lazyInit = getBeanFactory().getBeanDefinition(webServerFactoryBeanName).isLazyInit();
			this.serverManager = ServerManager.get(webServerFactory, lazyInit);
		}
		initPropertySources();
	}
Copy the code

2.11 ApplicationRunner & CommandLineRunner

Execute the ApplicationRunner & CommandLineRunner’s Run () method.

private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceofCommandLineRunner) { callRunner((CommandLineRunner) runner, args); }}}Copy the code