The SpringApplication startup logic

Personally think this is also very important, incidentally here reference “Taro” blog in-depth study of the underlying source code, and then with a picture to help understand memory. Our Spring Boot version is 1.5.9, so your source code implementation may be different, but I think the overall idea of Spring Boot should be similar between different versions.

The constructor

The sources array contains only one element: demoApplication.class
public SpringApplication(Object... sources) {
		initialize(sources);
	}
	
 private void initialize(Object[] sources) {
		if(sources ! =null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}
     	// Check whether it is a Web environment, different versions of Spring Boot, implementation may be different
		this.webEnvironment = deduceWebEnvironment();
     	
     	/ / set ApplicationContextInitializer
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
     	// ApplicationListener List
		setListeners((Collection) 
                     getSpringFactoriesInstances(ApplicationListener.class));
     	
		this.mainApplicationClass = deduceMainApplicationClass();
}

	public void setInitializers( Collection
       > initializers) {
		this.initializers = newArrayList<ApplicationContextInitializer<? > > ();this.initializers.addAll(initializers);
	}
	
	public void setListeners(Collection
       > listeners) {
		this.listeners = newArrayList<ApplicationListener<? > > ();this.listeners.addAll(listeners);
	}


	// Here are some properties of the SpringApplication class

	private ConfigurableEnvironment environment;

	private Class<? extends ConfigurableApplicationContext> applicationContextClass;

	private boolean webEnvironment;

	privateList<ApplicationContextInitializer<? >> initializers;privateList<ApplicationListener<? >> listeners;Copy the code

! [] (liutianruo-2019-go-go-go.oss-cn-shanghai.aliyuncs.com/notes/02_Sp… Application constructor. PNG)

run()

This is the core code to Boot Spring Boot.

	public ConfigurableApplicationContext run(String... args) {
        // It is used to match the start time of log statistics
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
        
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
        // AwT is related and can be ignored
		configureHeadlessProperty();
        / / get SpringApplicationRunListeners (encapsulates the multiple SpringApplicationRunListener)
		SpringApplicationRunListeners listeners = getRunListeners(args);
        // Start the listener
		listeners.starting();
        
		try {
            // Create an ApplicationArguments object
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            	
            // Load the property configuration. After execution, all environment properties are loaded, including application.properties and external property configurations
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
            // It is not important to display the Spring Boot pattern on the console
			Banner printedBanner = printBanner(environment);
            // Spring context
			context = createApplicationContext();
            // Exception reporter
			analyzers = new FailureAnalyzers(context);
            // Call initialize for all classes
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
            // Initialize the Spring context
			refreshContext(context);
            // Perform the initialization postlogic of the Spring context
			afterRefresh(context, applicationArguments);
            // Notify the listener that the Spring context has been started
			listeners.finished(context, null);
            // The statistics duration ends
			stopWatch.stop();
            
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, analyzers, ex);
			throw newIllegalStateException(ex); }}Copy the code

SpringApplicationRunListeners

	private SpringApplicationRunListeners getRunListeners(String[] args) { Class<? >[] types =newClass<? >[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}


Copy the code

getSpringFactoriesInstances

		private<T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<? >[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<String>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
Copy the code

SpringFactoriesLoader get meta-inf/spring. Factories configured SpringApplicationRunListener implementation class corresponding to the full name of the class, will use the Set come here.

org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
Copy the code
	SpringApplicationRunListeners(Log log,
			Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<SpringApplicationRunListener>(listeners);
	}
Copy the code

After got the SpringApplicationRunListener implementation class, is SpringApplicationRunListeners packaged as a List.

EventPublishingRunListener

An event listener is essentially an event announcer, encapsulated in an ApplicationListener. Can only say EventPublishingRunListener is only a code farmer just spend money to find someone else to write code, well, that SpringApplicationRunListeners is the company’s boss. Ha ha

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();
        // Get the ApplicationListener List wrapped in the SpringApplication constructor,
        / / and inject the List to the impleApplicationEventMulticaster
		for(ApplicationListener<? > listener : application.getListeners()) {this.initialMulticaster.addApplicationListener(listener); }}Copy the code

SimpleApplicationEventMulticaster site is at the bottom of the event listeners, really can hide, the tamilian boy.

Look at the EvenPublishingRunListener about event handling.

	@Override
	@SuppressWarnings("deprecation")
	public void starting(a) {
        // The direct broadcast event is gone
		this.initialMulticaster
				.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
	}

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

So let’s verify that

SpringApplicationRunListeners#starting

	public void starting(a) {
        / / there are only a EventPublishingRunListener internal implementation
		for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); }}Copy the code
  • SpringApplicationRunListeners.starting

    • SpringApplicationRunListener.starting

      • EventPublishingRunListener
        • SimpleApplicationEventMulticaster.multicastEvent
          • List<ApplicationListener>
      	@Override
      	public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) { ResolvableType type = (eventType ! =null ? eventType : resolveDefaultEventType(event));
      		for (finalApplicationListener<? > listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor();if(executor ! =null) {
      				executor.execute(new Runnable() {
      					@Override
      					public void run(a) { invokeListener(listener, event); }}); }else{ invokeListener(listener, event); }}}Copy the code

Leave a picture:

At this point, the ApplicationListener encapsulates so many layers that I have to deal with it, and we’re pretty much done.

ConfigurableEnvironment

	private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
        
		configureEnvironment(environment, applicationArguments.getSourceArgs());
        
		listeners.environmentPrepared(environment);
        
		if (!this.webEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
        
		return environment;
	}
Copy the code

getOrCreateEnvironment

	private ConfigurableEnvironment getOrCreateEnvironment(a) {
		if (this.environment ! =null) {
			return this.environment;
		}
        / / if it is a Web environment, creates StandardServletEnvironment
		if (this.webEnvironment) {
			return new StandardServletEnvironment();
		}
		return new StandardEnvironment();
	}
Copy the code

Note: our Spring Boot version 1.5, at the bottom of the Spring Framework version is 4.3, so there is no WebFlux (Since 5.0), so there is no StandardReactiveWebEnvironment

configureEnvironment

	protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}
Copy the code
configurePropertySources

MutablePropertySources

	protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
		MutablePropertySources sources = environment.getPropertySources();
        
        / / configuration defaultProperties
		if (this.defaultProperties ! =null&&!this.defaultProperties.isEmpty()) {
			sources.addLast(
					new MapPropertySource("defaultProperties".this.defaultProperties));
		}
        // The source of properties for the start parameter
		if (this.addCommandLineProperties && args.length > 0) {
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
            // If it already exists, replace it
			if(sources.contains(name)) { PropertySource<? > source = sources.get(name); CompositePropertySource composite =new CompositePropertySource(name);
				composite.addPropertySource(new SimpleCommandLinePropertySource(
						name + "-" + args.hashCode(), args));
				composite.addPropertySource(source);
				sources.replace(name, composite);
			}
			else {
                // If it does not exist, add
				sources.addFirst(newSimpleCommandLinePropertySource(args)); }}}Copy the code

private boolean addCommandLineProperties = true;

Whether to add JVM startup parameters. The default is add

configureProfiles
	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
		environment.getActiveProfiles(); // ensure they are initialized
		// But these ones should go first (last wins in a property key clash)
		Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles);
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
		environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
	}
Copy the code

Give me another picture

! [] (liutianruo-2019-go-go-go.oss-cn-shanghai.aliyuncs.com/notes/02_Sp… Application Environment.png)

createApplicationContext

 	
		/** * The class name of application context that will be used by default for web * environments. */
	public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";

	private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet"."org.springframework.web.context.ConfigurableWebApplicationContext" };

 protected ConfigurableApplicationContext createApplicationContext(a) { Class<? > contextClass =this.applicationContextClass;
		if (contextClass == null) {
			try {
                // Get the Class object based on reflection
				contextClass = Class.forName(this.webEnvironment
						? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass", ex); }}/ / according to the Class object instantiated ConfigurableApplicationContext object
		return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
	}
Copy the code

prepareContext

	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // ApplicationContext is associated with Environment
		context.setEnvironment(environment);
        // Set some properties of the context
		postProcessApplicationContext(context);
        / / initialize ApplicationContextInitializer
		applyInitializers(context);
        
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot Specific Singleton beans Set beanFactory properties
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if(printedBanner ! =null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
        / / load BeanDefinition
		Set<Object> sources = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[sources.size()]));
		listeners.contextLoaded(context);
	}
Copy the code

setEnvironment

Set the Environment property of ApplicationContext.

postProcessApplicationContext

Set some properties of ApplicationContext

	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		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()); }}}Copy the code

applyInitializers

	protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
            / / check ApplicationContextInitializer generics is not emptyClass<? > requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context,"Unable to call initializer.");
            / / ApplicationContextInitializer performs initialization logicinitializer.initialize(context); }}Copy the code
getInitializers
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
Copy the code

In SpringApplication constructor implementation, the initialization ApplicationContextInitializer, how to initialize, From the meta-inf/spring. Factories in traverse attribute is org. Springframework. Context. ApplicationContextInitializer corresponding attribute values.

refreshContext

Start the Spring container

	private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.}}}protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}	
Copy the code

org.springframework.context.support.AbstractApplicationContext#refresh

See, Spring the Boot just encapsulates the API, for the Environment to do the custom to create, also set up some attribute of the ApplicationContext, callback ApplicationContextInitializer implementation, Finally start the container or with the aid of the Spring Framework AbstractApplicationContext. Refresh to complete.

Include event listener is also, finally to actually listen to the ApplicationListener is also from the Spring Framework, so the Spring Boot source code, to the back, is the Spring Framework itself.

afterRefresh

callRunners

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

These two interfaces perform callback methods

Summary in one chart

! [] (liutianruo-2019-go-go-go.oss-cn-shanghai.aliyuncs.com/notes/02_Sp… Application integrity. PNG)