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