Inventory springboot: application main process

Posted by rajan on Sat, 19 Feb 2022 08:05:43 +0100

I preface

This article introduces the main process of SpringBoot Application

The main process entry of spring application is very simple:

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

1 > use @SpringBootApplication Note, indicate yes Spring Boot Application. Through it, you can turn on the function of automatic configuration.
2 > main method : call SpringApplication#Run (class <? >... Primarysources) method to start the Spring Boot application

Copy code

Let's take a step-by-step look at what has been done in this process, which mainly involves these things:

  • Create and start listener
  • Generate environment via args
  • Create context through environment
  • Refresh context
  • Of course, Banner will also be printed

flow chart

II Process analysis

2.1 spring application core process

The core process is mainly in spring application Class, let's look at this process:

SpringApplication properties

F- resourceLoader
    ?- Resource loader
F- primarySources 
    ?- major Java Config Array of classes
F- webApplicationType 
    ?- call WebApplicationType#The deduceFromClasspath() method judges the type of Web application through the classpath.
F- listeners 
    ?- ApplicationListener Array.
F- mainApplicationClass 
    ?- call #deduceMainApplicationClass() Method to get which is called #main(String[] args) method
    
    
// Three ApplicationContext loading classes are provided, 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 code

SpringApplication run method main process

M1_01- run()
        T-> time ,establish StopWatch  And start , Used to record the start-up time
		-> configureHeadlessProperty() : to configure headless attribute
            ?- Headless Mode is a configuration mode of the system. In this mode, the system lacks a display device, keyboard or mouse
            ?- In this mode, lightweight components can be created , Collect fonts and other pre work
		- getRunListeners : obtain SpringApplicationRunListeners ,And turn on monitoring listeners.starting()
		- 1 establish  ApplicationArguments object
		- 2 prepareEnvironment Load attribute configuration(afferent listener + arguments ) -> M20_01
                    ?- After execution, all environment All properties will be loaded (application.properties etc.)
		- 3 Print banner(printBanner) 
		- 4 establish Spring container(createApplicationContext)
                -   Prepare exception object(getSpringFactoriesInstances.SpringBootExceptionReporter )
		- 5 Call all initialization classes initialize method(prepareContext) , initialization Spring container
		- 6 Refresh container(refreshContext) , implement Spring Post logic for container initialization(afterRefresh)
         T-> Timing complete    
		- 7 notice SpringApplicationRunListener  , Perform closure such as exception handling

Copy code

Spring application mainstream pseudo code

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 get ConfigurableEnvironment
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		// PS:M1_01_03
		context = createApplicationContext();
                // Get exception M1 from factories_ eleven
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
                // M1_35: add attribute for Context
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // M1_50: refresh container Bean
		refreshContext(context);
		afterRefresh(context, applicationArguments);
                // End of timing
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
                
                // Execute the 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 code

2.2 sub module: Enviroment processing

M1_21- prepareEnvironment
    - getOrCreateEnvironment -> M21_01
    - 

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
    // Internally generate different environments through WebApplicationType (you can set your own Environment) - > M1_ twenty-three
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // Override this method to fully control environment customization, or override one of the above methods to fine-grained control the attribute source or profile respectively. - > M1_ twenty-four
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // Process the configurationProperties property - > M2_ 01
    ConfigurationPropertySources.attach(environment);
    // listener processing
    listeners.environmentPrepared(environment);
    // Bind environment to spring application
    bindToSpringApplication(environment); -> M1_25
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

M1_23- getOrCreateEnvironment
    - adopt webApplicationType , Create three different Environment

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


M1_24- configureEnvironment
    - obtain ApplicationConversionService Implementation class of
    - call configurePropertySources(environment, args) , Add, remove, or reorder any in this application environment PropertySources. 
        - MutablePropertySources sources = environment.getPropertySources();
            ?- Get previous MutablePropertySources
	- sources.addLast : add to defaultProperties
	- If args > 0 , And you can add commonLine , Then add CommandLineProperties
            ?- The attribute will be judged commandLineArgs Will it exist ,Common replacement methods exist
            
// 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() : obtain MutablePropertySources
	- attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME) : obtain PropertySource
        - If attached by null Or not equal to sources , Will sources Replace the original attached
        
// Binder: a container object that binds 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, boolean create)
        - context.clearConfigurationProperty() : Clear original attribute
        - handler.onStart(name, target, context) : BindHandler Start binding
        - bindObject : Bind properties to objects
        - handleBindResult : Return binding result
   
   
private <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) {
        return handleBindError(name, target, handler, context, ex);
    }
}
        

Copy code

configureIgnoreBeanInfo configuration

// Only 2 properties are set: 

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

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

//So what is this attribute for? - > spring. beaninfo. ignore
 When the value is true Time , Means to skip BeanInfo Class search . 
If you've experienced what doesn't exist BeanInfo Duplication of classes ClassLoader To access, consider switching this flag to“ true",In case this access is expensive during startup or delayed loading

But at this stage, all BeanInfo Metadata class. The default value is"false"

Copy code

2.3 Banner printing

Pure curiosity, come and have a look

M1_28- printBanner : prepare 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);
}

// Core class SpringBootBanner
C- SpringBootBanner
    M- printBanner
        - printStream.println(line) : Print that line by line Spring
        - Print version number
            ?-  :: Spring Boot ::        (v2.3.1.RELEASE)

// ps: just click in and print line by line

Copy code
  • A switch is provided
  • Build a SpringApplicationBannerPrinter,
  • Calling print generates a Banner object

2.4 createApplicationContext logic

This logic is related to the creation of ApplicationContext. Here is a brief introduction:

C- SpringApplication
    M1_30- createApplicationContext
    	- Class<?> contextClass = this.applicationContextClass;
	    IF- contextClass by null
         	- according to webApplicationType Type, get ApplicationContext type
            - AnnotationConfigServletWebServerApplicationContext 
            - AnnotationConfigApplicationContext
            - AnnotationConfigReactiveWebServerApplicationContext
         - according to contextClass establish ApplicationContext object   
         
         
// M1_30 pseudo code
protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                 // Prepare different contextclasses according to 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 obtains the context implementation class
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    
    
// The core process of ApplicationContext is worth exploring. A single chapter will be opened later to explain in detail
    

Copy code

PS:M1_01_03 generate a ConfigurableApplicationContext

PS: ApplicationContext architecture

Here's the difference between Reactive

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

WebFlux is not a substitute for Spring MVC. It is mainly used in asynchronous non blocking programming model. Applications using WebFlux have shorter overall response time, fewer threads to start and less memory resources to use. At the same time, the greater the delay, the more obvious the advantages of WebFlux.

Please refer to this document for details@ blog.csdn.net/u010862794/...

2.5 intermediate operation

M1_11 get the processing class of SpringBootExceptionReporter

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


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

Copy code

M1_12 callRunners: what are you doing

This method mainly 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 instanceof CommandLineRunner) {
            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 <-------");
    }
}

// To put it simply, the initialization and startup of ApplicationRunner is done here



Copy code

2.6 three processing of context

The secondary processing of Context is divided into three steps:

  • prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    • Set the pre property of Context
  • refreshContext(context);
    • Inject Bean, register listener, initialize and publish events
  • afterRefresh(context, applicationArguments);
    • Empty implementation at this stage

Main process of prepareContext

    // prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    M1_35- prepareContext : prepare ApplicationContext Object, mainly to initialize some of its properties
        -> 1 set up context of environment attribute
        -> 2 call #Postprocessapplicationcontext (configurableapplicationcontext) method,
            ?- set up context Some properties of -> M1_36
        -> 3 call #Applyinitializers (configurableapplicationcontext) method,
            ?- initialization ApplicationContextInitializer -> applyInitializers
        -> 4 call SpringApplicationRunListeners#contextPrepared
            ?- notice SpringApplicationRunListener Array of, Spring Container preparation completed
        -> 5 set up beanFactory Properties of
        -> 6 call #Load (ApplicationContext, object [] sources) method to load BeanDefinition
            ?- 
            -> establish BeanDefinitionRegistry object
            -> set up loader attribute
            -> implement BeanDefine load

// M1_35 pseudo code
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        //  Set the environment for the container
        context.setEnvironment(environment);
        // Set the container classloader and conversionService, that is, the loading tool of the classes in the container
        postProcessApplicationContext(context);
        // Apply any ApplicationContextInitializers to the context before refreshing it
        applyInitializers(context);
        // The listeners execution is called after the ApplicationContext is created and ready, but is called before loading the source.
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
             // Print startup log configuration
            logStartupInfo(context.getParent() == null);
             // Print active profile information
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        // Bean factory injects related 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 beanfactoryprocessor
            // The new beanfactoryprocessor will be applied to the internal Bean factory of this application context before refreshing, and then any Bean definitions will be evaluated. 
            // 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 : stay ApplicationContext Apply any relevant post-processing in
	- beanNameGenerator Set to if present context.getBeanFactory().registerSingleton in
	- resourceLoader Exists and is GenericApplicationContext type , be setResourceLoader
	- resourceLoader Exists and is resourceLoader be setClassLoader
	- addConversionService by true Set to BeanFactory in
     
// 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()) : for Cyclic processing Initializers
        - Assert Judge whether it is ApplicationContextInitializer Examples of
        - initializer.initialize(context) Initialize object
    
            
M1_38- load
    - Here is mainly to create BeanDefinitionLoader
        
protected void load(ApplicationContext context, Object[] sources) {
    // Create BeanDefinitionLoader and directly create new 
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        // beanName build class
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    // -> M5_01
    loader.load();
} 

C5- BeanDefinitionLoader
    M5_01- loader
        ?- There will be a loop call load source
 
// M5_01 pseudo code
for (Object source : this.sources) {
    count += load(source);
}   

Copy code

refresh process

C- AbstractApplicationContext
    M1_50- refreshContext(context);
        - refresh(context);
        - If so ShutdownHook (shutdown hook ) , Then register registerShutdownHook


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

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

            // Tell subclasses to refresh internal bean factories
            prepareBeanFactory(beanFactory);

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

                // Call the factory processor registered as a bean in the context
                // The main logic of IOC is in it
                invokeBeanFactoryPostProcessors(beanFactory);

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

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

                // Initialize the event multimaster for this context
                initApplicationEventMulticaster();

                // Initializes other special bean s in subclasses of a specific context
                onRefresh();

                // Check the listener bean s and register them
                registerListeners();

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

                //The last step: publish the corresponding event
                finishRefresh();
            }catch (BeansException ex) {
            
                // Destroy the created singleton to avoid hanging resources
                destroyBeans();

                // Reset the activity flag
                cancelRefresh(ex);

                // Propagate 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 is also the main process of AbstractApplicationContext, which will be described in the subsequent ApplicationContext chapter
    

Copy code

afterRefresh process

PS: This is an empty implementation

M1_60- afterRefresh


Copy code

2.7 Listener processing

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 here, ha ha ha ha ha
It can be seen that the purpose of step 1 and step 4 is relatively clear. Let's mainly look at step 2 Three steps

getRunListeners

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

Copy code

listener.starting

//The starting here is the running of the eventpublishing runlistener
// For the processing logic of specific events, we will continue to go deep into subsequent documents

C- EventPublishingRunListener
	M- starting()
		- this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	
C- AbstractApplicationEventMulticaster
	?- Multicast all events to all registered listeners and call them in the calling thread. , Simply put, it is mass incidents
	
	
	

Copy code

listeners.started(context)

// There are more events here

for (SpringApplicationRunListener listener : this.listeners) {
	listener.started(context);
}

Here or EventPublishingRunListener
    
@Override
public void started(ConfigurableApplicationContext context) {
	context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
	AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}    


Copy code

listener.running

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

Due to the limited space, I won't take a detailed look at what is implemented

2.8 other treatment

	
	
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
			Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
    try {
        try {
            // Process 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 code

III summary

This article briefly reviews the general process of SpringBoot. The space is limited. Several points have not been included for the time being, and will be supplemented later

  • @Spring application annotation
  • The beginning and end of SpringApplicationContext
  • Listener loading logic


Author: ant black
Link: https://juejin.cn/post/6956231946653401124
Source: Nuggets
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Topics: Java Spring Spring Boot AOP ioc