General documentation: Article directory Github: github.com/black-ant

A. The preface

This article takes a look at the main process of SpringBoot Application.

The main process entry for SpringAppliation is simple:

@SpringBootApplication
public class BaseApplication {
    public static void main(String[] args) { SpringApplication.run(BaseApplication.class, args); }}1> use@SpringBootApplicationNote indicating that it is a Spring Boot application. You can enable the automatic configuration function.2SpringApplication#run(Class<? >... PrimarySources) method to start the Spring Boot applicationCopy the code

Let’s take a step by step look at what is done in this process from this entrance, which mainly involves the following things:

  • Create and start a listener
  • Generate environment through ARGS
  • Context is created using environment
  • Refresh the context
  • And, of course, you print the Banner

The flow chart

2. Process analysis

2.1 core process of SpringApplication

The core process is mainly in springApplication.class. Let’s look at this process:

SpringApplication properties

F- resourceLoader ? - Resource loader f-Primarysources? - An array of the main Java Config classes F- webApplicationType? - Call the WebApplicationType#deduceFromClasspath() method to determine the Web application type by classpath. F- listeners ? - ApplicationListener array. F- mainApplicationClass ? - call # deduceMainApplicationClass () method, which is called the # main (String [] args) method// Provides three ApplicationContext loading classes, which will be used later
String DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext";
String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";


// Default Banner address -> "banner.txt"
String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
    

Copy the code

SpringApplication Run method main process

M1_01 - the run () T - > time, create a StopWatch and start, start to record the length of the - > configureHeadlessProperty () : the headless attribute configuration? - Headless is a configuration mode of the system. In this mode, is the system missing a display device, keyboard, or mouse? - in this mode can create lightweight components, collect front work on the font - getRunListeners: get SpringApplicationRunListeners, and open to monitor listeners. The starting ()1Create the ApplicationArguments object -2PrepareEnvironment load property configuration (pass listener + arguments) -> M20_01? - When the execution is complete, all environment attributes (application.properties, etc.) will be loaded -3Print the banner (printBanner) -4Create the Spring container (createApplicationContext) - (getSpringFactoriesInstances. SpringBootExceptionReporter) - ready to object5Initialize the Spring container - by calling the initialize method (prepareContext) of all initialization classes6Refresh the Spring container, perform the afterRefresh logic of the Spring container initialization T-> timing complete -7Notify SpringApplicationRunListener, executing exception handling closingCopy the code

Pseudocode of the SpringApplication main process

public ConfigurableApplicationContext run(String... args) throws Exception {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                // Call M1_21 to get a ConfigurableEnvironment
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		// PS:M1_01_03
		context = createApplicationContext();
                // Get Exception M1_11 from the factories
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
                // M1_35: add attributes to Context
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // M1_50: refresh the container Bean
		refreshContext(context);
		afterRefresh(context, applicationArguments);
                // The timer is over
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
                
                // Implements ApplicationRunne object
		callRunners(context, applicationArguments);
	}catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}
	
	try {
		listeners.running(context);
	}catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}
Copy the code

2.2 Submodule: Enviroment processing

M1_21- prepareEnvironment
    - getOrCreateEnvironment -> M21_01
    - 

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
    // Internally generate different environments via WebApplicationType (you can set your own Environment) -> M1_23
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // Override this method for full control over environment customization, or override one of the above methods for fine-grained control over property sources or profiles, respectively. -> M1_24
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // Processing the configurationProperties -> M2_01
    ConfigurationPropertySources.attach(environment);
    / / listener
    listeners.environmentPrepared(environment);
    // bind environment to SpringApplication
    bindToSpringApplication(environment); -> M1_25
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    returnenvironment; } M1_23 -getorCreateEnvironment - Create three different environments with webApplicationType// M1_23 pseudocode
private ConfigurableEnvironment getOrCreateEnvironment(a) {
    if (this.environment ! =null) {
        return this.environment;
    }
    switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return newStandardEnvironment(); }} M1_24 - configureEnvironment - get ApplicationConversionService implementation class - call configurePropertySources (environment, Args) to add, remove, or reorder any PropertySources in this application environment. - MutablePropertySources sources = environment.getPropertySources(); ? - Get previous MutablePropertysources-sources. AddLast: Add defaultProperties - if args >0, and can add commonLine, then add CommandLineProperties? - This determines whether the property commandLineArgs will exist. If it does, it is often replaced// M1_24 pseudo-code
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    if (this.addConversionService) { ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService) conversionService); } configurePropertySources(environment, args); configureProfiles(environment, args); } C2- ConfigurationPropertySources. M2_01- attach(environment); - sources = ((ConfigurableEnvironment) environment).getPropertySources() : Get (ATTACHED_PROPERTY_SOURCE_NAME) : Gets PropertySource - If attached isnullOr is not equal to sources, then sources is replaced with the original attached// Binder: container objects that bind objects from one or more containers
M1_25- bindToSpringApplication
C3- Binder
    M3_01- bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,boolean allowRecursiveBinding, booleanCreate) - context. ClearConfigurationProperty () : to empty the original attributes - handler. OnStart (name, target, the context) : BindHandler starts binding. -bindobject: binds properties to objects. -HandleBindresult: returns binding resultsprivate <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,boolean allowRecursiveBinding, boolean create) {
    try {
        Bindable<T> replacementTarget = handler.onStart(name, target, context);
        if (replacementTarget == null) {
            return handleBindResult(name, target, handler, context, null, create);
        }
        target = replacementTarget;
        Object bound = bindObject(name, target, handler, context, allowRecursiveBinding);
        return handleBindResult(name, target, handler, context, bound, create);
    }catch (Exception ex) {
        returnhandleBindError(name, target, handler, context, ex); }}Copy the code

ConfigureIgnoreBeanInfo configuration

// Only two properties are set:

// Attribute 1: spring.beaninfo.ignore, for
environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE)

// Set 2: set to System
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString())

// What is this attribute for? -> spring.beaninfo.ignoreThe duty oftrueIf you experience repeated ClassLoader accesses to a nonexistent BeanInfo class, consider switching this flag to"trueBut for all BeanInfo metadata classes at this stage, the default is"false"

Copy the code

2.3 Banner printing

Just out of curiosity. Check it out

M1_28 -printBanner: Prepare the Banner class// Code details
private Banner printBanner(ConfigurableEnvironment environment) {
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    ResourceLoader resourceLoader = (this.resourceLoader ! =null)?this.resourceLoader
        : new DefaultResourceLoader(getClassLoader());
        
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    if (this.bannerMode == Mode.LOG) {
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    // Core code -> call class SpringBootBanner
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

// The core class SpringBootBannerC - SpringBootBanner M - printBanner - printStream println (line) : line by line print the Spring - print version number? - :: Spring Boot :: (v23.1..RELEASE)

// ps: print line by line

Copy the code
  • A switch is provided
  • Build a SpringApplicationBannerPrinter,
  • The call to print generates a Banner object

2.4 createApplicationContext logic

This logic is related to creating ApplicationContext, which is briefly described here:

C- SpringApplication M1_30- createApplicationContext - Class<? > contextClass =this.applicationContextClass; IF - contextClass fornull- Based on the webApplicationType type, - AnnotationConfigServletWebServerApplicationContext - AnnotationConfigApplicationContext - get ApplicationContext type AnnotationConfigReactiveWebServerApplicationContext - based on contextClass create ApplicationContext object// M1_30 pseudo-code
protected ConfigurableApplicationContext createApplicationContext(a) { Class<? > contextClass =this.applicationContextClass;
        if (contextClass == null) {
            try {
                 // Prepare different Contextclasses for different WebApplicationTypes
                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); }}// The reflection constructor method gets the context implementation class
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    
    
// The core process of ApplicationContext is worth going into more detail
    

Copy the code

PS: generate a ConfigurableApplicationContext M1_01_03

PS: ApplicationContext architecture

Just to make a distinction about Reactive

As shown in the figure, Reactive is an important technology stack in Spring, and Reactive can be used to build responsive, resilient, resilient and message-driven enterprise response systems.

WebFlux is not an alternative to Spring MVC. It mainly applies to the asynchronous non-blocking programming model. Applications that use WebFlux have shorter overall response times, fewer threads to start, and less memory resources to use. At the same time, the greater the latency, the greater the benefits of WebFlux.

Specific can refer to this document @ blog.csdn.net/u010862794/…

2.5 Intermediate Operations

M1_11 get SpringBootExceptionReporter processing class

getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);


// Spring.factories
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

Copy the code

M1_12 callRunners: What for

This method basically runs ApplicationRunner

// callRunners main process
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); }}}@Component
public class SourceTestLogic implements ApplicationRunner {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void run(ApplicationArguments args) throws Exception {
        logger.info("------> run <-------"); }}// In short, this is what ApplicationRunner initializes



Copy the code

2.6 Third processing of Context

The secondary processing of Context is divided into three steps:

  • prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    • Set the Context front property
  • refreshContext(context);
    • Inject the Bean, register the listener, initialize and publish events
  • afterRefresh(context, applicationArguments);
    • At this stage, the implementation is empty

The main process of prepareContext

    // prepareContext(context, environment, listeners, applicationArguments, printedBanner);M1_35- prepareContext: Prepare an ApplicationContext object by initializing its properties ->1Set context environment property ->2Method, called # postProcessApplicationContext (ConfigurableApplicationContext context)? - set some properties of context -> M1_36 ->3Method, called # applyInitializers (ConfigurableApplicationContext context)? - initialize ApplicationContextInitializer - > applyInitializers - >4Call SpringApplicationRunListeners# contextPrepared? - inform SpringApplicationRunListener array, the Spring container to complete - >5Set beanFactory properties ->6#load(ApplicationContext context, Object[] sources); -- > Create BeanDefinitionRegistry object -> Set Loader properties -> Execute BeanDefine load// M1_35 pseudocode
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // Set environment for the container
        context.setEnvironment(environment);
        // Set the container classLoader and conversionService, a tool that loads classes in the container
        postProcessApplicationContext(context);
        / / before the refresh context, any ApplicationContextInitializers was applied to the context
        applyInitializers(context);
        // Listeners, called after the ApplicationContext is created and ready, but before the source is loaded.
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
             // Print the startup log configuration
            logStartupInfo(context.getParent() == null);
             // Prints the active Profile information
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        // beanFactory is used to inject 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 (this.lazyInitialization) {
            // Add a new BeanFactoryPostProcessor
            // The new BeanFactoryPostProcessor will apply the internal Bean factory to this application context before refreshing, and then evaluate any Bean definitions.
            // called during context configuration.
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // -> M1_38
        load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); } M1_36- postProcessApplicationContext : Apply any associated post-processing in the ApplicationContext - beanNameGenerator is present and set to context.getBeanFactory().registerSingleton - resourceLoader And for GenericApplicationContext type, If setResourceLoader - resourceLoader exists and is resourceLoader, setClassLoader - addConversionService istrueIs set to BeanFactory// M1_36 pseudo-code
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()); }}if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
    }
}
    

M1_37- applyInitializers
    FOR-  (ApplicationContextInitializer initializer : getInitializers()) : forCirculation processing is for ApplicationContextInitializer instance Initializers - Assert -, initializer. The initialize (context) M1_38 - load - initialize the object This is mainly creatingBeanDefinitionLoader
        
protected void load(ApplicationContext context, Object[] sources) {
    // Create BeanDefinitionLoader with new definitionLoader
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator ! =null) {
        // beanName generates the class
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader ! =null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment ! =null) {
        loader.setEnvironment(this.environment);
    }
    // -> M5_01loader.load(); } C5- BeanDefinitionLoader M5_01- loader ? - This will loop through the load source// M5_01 pseudo-code
for (Object source : this.sources) {
    count += load(source);
}   

Copy the code

The refresh process

C- AbstractApplicationContext M1_50- refreshContext(context); - refresh(context); - registerShutdownHook, if any@Override
    public void refresh(a) throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for a refresh.
            prepareRefresh();

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

            // Tell the subclass to refresh the internal bean factory
            prepareBeanFactory(beanFactory);

            try {
                // Allow post-processing of bean factories in context subclasses
                postProcessBeanFactory(beanFactory);

                // Call the factory handler registered as a bean in the context
                // This is where the main logic of IOC lies
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register the Bean handler created by the intercepting Bean
                registerBeanPostProcessors(beanFactory);

                // Initialize the message source for this context
                initMessageSource();

                // Initialize the event multi-master for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in a particular context subclass.
                onRefresh();

                // Check the listener beans and register them.
                registerListeners();

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

                // Last step: publish the corresponding event
                finishRefresh();
            }catch (BeansException ex) {
            
                // Destroy the created singleton to prevent the resource from dangling
                destroyBeans();

                // Reset the "active" flag.
                cancelRefresh(ex);

                // Propagates the exception to the caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...resetCommonCaches(); }}}/ / this part also is the main process, AbstractApplicationContext on subsequent ApplicationContext ChanZhang said
    

Copy the code

AfterRefresh process

PS: This is an empty implementation

M1_60- afterRefresh


Copy the code

2.7 the Listener to handle

When the Application starts, the Listener involves a total of four operations:

  • getRunListeners
  • listener.starting
  • listener.started
  • listener.running

With my poor English level, I’m afraid there is a continuous tense and a past tense in it. As you can see, the first step and the fourth step are quite clear. Let’s take a look at the second step. Three steps

getRunListeners

C- SpringApplication
	M- getRunListeners
		- getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)
		
/ / as you can see, obtained from the Factories, only one EventPublishingRunListener first stage
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

Copy the code

listener.starting

/ / the starting here is EventPublishingRunListener run
// The details of the event processing logic, we will follow the document further

C- EventPublishingRunListener
	M- starting()
		- this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); C- AbstractApplicationEventMulticaster ? - Multicast all events to all registered listeners and invoke them in the calling thread, simply called event massCopy the code

listeners.started(context)

// There are more events here

for (SpringApplicationRunListener listener : this.listeners) { listener.started(context); Or EventPublishingRunListener} here@Override
public void started(ConfigurableApplicationContext context) {
	context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
	AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}    


Copy the code

listener.running

/ / release ReadyEvent
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
Copy the code

Space is limited, so I won’t go into the details of which implementations

2.8 Other Processing


	
	
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection
       
         exceptionReporters, SpringApplicationRunListeners listeners)
        {
    try {
        try {
            // Process the exit code
            handleExitCode(context, exception);
            if(listeners ! =null) { listeners.failed(context, exception); }}finally {
            reportFailure(exceptionReporters, exception);
            if(context ! =null) { context.close(); }}}catch (Exception ex) {
        logger.warn("Unable to close ApplicationContext", ex);
    }
    ReflectionUtils.rethrowRuntimeException(exception);
}
	
Copy the code

3. Summary

This article is a brief review of the general process of SpringBoot. Due to limited space, a few points have not been included for the time being and will be supplemented in the future

  • @ SpringApplication annotations
  • The information of SpringApplicationContext
  • Load logic of the Listener