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.