SpringBoot concept

  • SpringApplication provides a convenient way to start spring applications from the main method.
  • Spring Boot is based on Spring 4.0

The characteristics of

  • Rapid development and construction of microservice systems
  • Embedded containers, such as Tomcat and Undertow
  • Automatic loading
  • Auto manage dependencies

The introduction

  • So the above springboot has so many benefits, that first study springboot is how to start, and then we sub-study automatic loading

Start analyzing the source code

@SpringBootApplication(scanBasePackages = {"com.github.demo"}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }} / / SpringApplication run method public static ConfigurableApplicationContext run (Class <? > primarySource, String... args) { return run(new Class<? >[] { primarySource }, args); } / / final call SpringApplication run method public static ConfigurableApplicationContext run (Class <? >[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }Copy the code

Initialize SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<? >... PrimarySources) {// set resource this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); / / initialize type Is a web or reactor enclosing webApplicationType = webApplicationType. DeduceFromClasspath (); / / from the classpath find META ‐ INF/spring. Factories file, load ApplicationContextInitializer interface implementation class, And set the setInitializers ((Collection) getSpringFactoriesInstances (ApplicationContextInitializer. Class)); // Find the implementation class of the ApplicationListener interface in the META‐INF/ Spring. factories file. And set the setListeners ((Collection) getSpringFactoriesInstances (ApplicationListener. Class)); this.mainApplicationClass = deduceMainApplicationClass(); }Copy the code

ApplicationContextInitializer

define

Before the Spring container to refresh, initialization callback interface ApplicationContextInitializer ConfigurableApplicationContext context is the product of the original Spring framework

The implementation class
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
Copy the code

ApplicationListener

define

ApplicationListener: Listens for events published in the Spring container

The implementation class
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
Copy the code

How the core works

/ / run springboot applications, create and refresh the ApplicationContext. Public ConfigurableApplicationContext run (String... Args) {// 1. StopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; / / 2. Support report spring application startup errors The key Collection < SpringBootExceptionReporter > exceptionReporters = new ArrayList < > (); configureHeadlessProperty(); / / 3. Get the listener key SpringApplicationRunListeners listeners = getRunListeners (args); // 3.1 Method emphasis of calling the event on startup Try {/ / 4. Key ApplicationArguments ApplicationArguments ApplicationArguments = new DefaultApplicationArguments (args); // The process parameters are configurable = prepareEnvironment(Listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 6. PrintedBanner = printBanner(environment); // createApplicationContext context = createApplicationContext(); / / 8. Through spi access error reporting of key exceptionReporters = getSpringFactoriesInstances (SpringBootExceptionReporter. Class, new Class[] { ConfigurableApplicationContext.class }, context); PrepareContext (context, environment, listeners, applicationArguments, printedBanner); // 10. RefreshContext (context); AfterRefresh (context, applicationArguments); // 12. Stopwatch.stop (); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); Listeners. Started (context); // 14. Call callRunners(Context, applicationArguments) to implement ApplicationRunner and CommandLineRunner. } catch (Throwable ex) { // 15. HandleRunFailure (Context, ex, exceptionReporters, Listeners) throw new IllegalStateException(ex); // Listeners. Running (context); } catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }Copy the code

3. Obtain event listening

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

Note: all the implementation by loading SpringApplicationRunListener SPI interface

SpringApplicationRunListener life cycle

define

The listener of the spring application’s run method

Seven stages
default void starting() {
}
default void environmentPrepared(ConfigurableEnvironment environment) {
}

default void contextPrepared(ConfigurableApplicationContext context) {
}

default void contextLoaded(ConfigurableApplicationContext context) {
}

default void started(ConfigurableApplicationContext context) {
}

default void running(ConfigurableApplicationContext context) {
}

default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
Copy the code
The implementation class

In the spring. Factories file defines SpringApplicationRunListener implementation class

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
Copy the code
EventPublishingRunListener class seven stages of the implementation of the interface
@ Override public void starting () {/ / events started this. InitialMulticaster. MulticastEvent (new ApplicationStartingEvent(this.application, this.args)); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); } @Override public void contextPrepared(ConfigurableApplicationContext context) { this.initialMulticaster .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context)); } @Override public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<? > listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context)); } @Override public void started(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context)); } @Override public void running(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context)); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception); if (context ! = null && context.isActive()) { // Listeners have been registered to the application context so we should // use it at this point if we can context.publishEvent(event); } else { // An inactive context may not have a multicaster so we use our multicaster to // call all of the context's listeners instead if (context instanceof AbstractApplicationContext) { for (ApplicationListener<? > listener : ((AbstractApplicationContext) context) .getApplicationListeners()) { this.initialMulticaster.addApplicationListener(listener); } } this.initialMulticaster.setErrorHandler(new LoggingErrorHandler()); this.initialMulticaster.multicastEvent(event); }}Copy the code

Note: EventPublishingRunListener class implementation is different in different stages of broadcast events

CommandLineRunner

define

Extension point belonging to a SpringBoot application that is executed after the SpringBoot application’s applicationContext is initialized

@FunctionalInterface
public interface CommandLineRunner {

   /**
    * Callback used to run the bean.
    * @param args incoming main method arguments
    * @throws Exception on error
    */
   void run(String... args) throws Exception;

}
Copy the code

ApplicationRunner

define

@FunctionalInterface
public interface ApplicationRunner {

   /**
    * Callback used to run the bean.
    * @param args incoming application arguments
    * @throws Exception on error
    */
   void run(ApplicationArguments args) throws Exception;

}
Copy the code

Build ApplicationArguments

public interface ApplicationArguments {
    String[] getSourceArgs();
    Set<String> getOptionNames();
    boolean containsOption(String name);
    List<String> getOptionValues(String name);
    List<String> getNonOptionArgs();
}
Copy the code
public class DefaultApplicationArguments implements ApplicationArguments { private final Source source; private final String[] args; public DefaultApplicationArguments(String... args) { Assert.notNull(args, "Args must not be null"); this.source = new Source(args); this.args = args; }... private static class Source extends SimpleCommandLinePropertySource { Source(String[] args) { super(args); }}}Copy the code

Create ApplicationContext

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() { Class<? > contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }Copy the code

Note:

  • This is simple, it is through the org. Springframework. Boot. WebApplicationType create different ApplicationContext

Preprocessing the context (prepareContext method)

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// Set the container variable context.setenvironment (environment); / / execution container rear handle postProcessApplicationContext (context); The old / / in the context, apply ApplicationContextInitializer to context. applyInitializers(context); Event listeners. / / broadcasting context have prepared contextPrepared (context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } / / Add the boot specific singleton beans / / in the container into applicationArguments bean ConfigurableListableBeanFactory the 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) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); Load (context, sources.toarray (new Object[0])); // Listeners loaded by the container. ContextLoaded (context); }Copy the code

Refresh context

private void refreshContext(ConfigurableApplicationContext context) { if (this.registerShutdownHook) { try { // Registered container closed before do some recycling operation context. RegisterShutdownHook (); } catch (AccessControlException ex) { // Not allowed in some environments. } } refresh(context); }Copy the code

Really call AbstractApplicationContext# refresh

protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}
Copy the code

AbstractApplicationContext# refresh this method is mainly the spring application context lifecycle approach, here only do a simple introduction, not to delve into the deep

public void refresh() throws BeansException, An IllegalStateException {synchronized (enclosing startupShutdownMonitor) {/ / for children to create the BeanFactory implementation. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

PrepareBeanFactory (BeanFactory); PostProcessBeanFactory (BeanFactory); postProcessBeanFactory(BeanFactory); / / I call to register for the BeanFactory factory invokeBeanFactoryPostProcessors processor (the BeanFactory); / / register rear bean processing class implementing the BeanPostProcessor interface registerBeanPostProcessors (the beanFactory); / / initializes the application to realize broadcasting initApplicationEventMulticaster (); // leave it to subclasses to implement the refresh operation onRefresh(); // Check bean listeners and register them. . registerListeners(); / / complete Bean initialization finishBeanFactoryInitialization (the beanFactory); FinishRefresh (); finishRefresh(); }catch (BeansException ex) { // Propagate exception to caller. throw ex; }finally { resetCommonCaches(); }}}Copy the code

Post processing

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
	}
Copy the code

This is an empty method that needs to be implemented by the subclass itself

Calls the method that starts the event

void started(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.started(context); }}Copy the code

Call the methods that implement ApplicationRunner and CommandLineRunner

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

conclusion

  • The observer model is used
  • SPI technology is used to dynamically load specific implementation classes, as is dubbo. To learn more about Dubbo’s SPI, see the following article :juejin.cn/post/697392…
  • SpringBoot starts the loader in two ways :CommandLineRunner and ApplicationRunner, and after the application has been started
  • CommandLineRunner and ApplicationRunner are executed in the same Order by adding them to the same list and sending them according to the @order annotation