1, Background
we use his three articles to interpret the refresh of application context and have a general outline of the whole refresh process. This article mainly interprets some operations after the refresh of application context. As usual, we still review the whole process started, so that we can not get lost.
1.1. Overall process of run method
the specific path of the class where the next few methods are located: org springframework. boot. SpringApplication
public ConfigurableApplicationContext run(String... args) { // 1. Record the start time of startup in nanoseconds long startTime = System.nanoTime(); // 2. Initialize startup context, initialize application context DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; // 3. Set the headless attribute: "java.awt.headless". The default value is: true (there is no graphical interface) configureHeadlessProperty(); // 4. Get all Spring run listeners SpringApplicationRunListeners listeners = getRunListeners(args); // Publish app start event listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 5. Initialize default application parameter class (command line parameter) ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 6. Prepare the Spring environment according to the running listener and application parameters ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // Configure ignore bean information configureIgnoreBeanInfo(environment); // 7. Create a Banner and print Banner printedBanner = printBanner(environment); // 8. Create application context context = createApplicationContext(); // Set applicationStartup context.setApplicationStartup(this.applicationStartup); // 9. Prepare application context prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 10. Refresh application context (core) refreshContext(context); // 11. Post processing of application context refresh afterRefresh(context, applicationArguments); // 13. Time information, output log record and execution main class name Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } // 14. Publish application context startup completion event listeners.started(context, timeTakenToStartup); // 15. Execute all Runner callRunners(context, applicationArguments); } catch (Throwable ex) { // Run error handling handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { // 16. Publish application context ready event (available) Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { // Run error handling handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } // 17. Return application context return context; }
1.2 scope of interpretation
the scope of this article is as follows:
// 11. Post processing of application context refresh afterRefresh(context, applicationArguments); // 13. Time information, output log record and execution main class name Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } // 14. Publish application context startup completion event listeners.started(context, timeTakenToStartup); // 15. Execute all Runner callRunners(context, applicationArguments); try { // 16. Publish application context ready event (available) Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { // Run error handling handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } // 17. Return application context return context;
2, Post processing of application context refresh
the specific path of the class where this method is located: org springframework. boot. SpringApplication
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { }
the post refresh in this is an empty implementation.
3, Time information, output log record and execution main class name
// Calculate the time from start to refresh Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); }
let's first look at the logStarted method. The specific path of the class where this method is located: org springframework. boot. StartupInfoLogger
class StartupInfoLogger { private final Class<?> sourceClass; StartupInfoLogger(Class<?> sourceClass) { this.sourceClass = sourceClass; } void logStarted(Log applicationLog, Duration timeTakenToStartup) { if (applicationLog.isInfoEnabled()) { // Get the startup information and output the log applicationLog.info(getStartedMessage(timeTakenToStartup)); } } private CharSequence getStartedMessage(Duration timeTakenToStartup) { StringBuilder message = new StringBuilder(); message.append("Started "); // Splice subclass information appendApplicationName(message); message.append(" in "); // Time to start the main class message.append(timeTakenToStartup.toMillis() / 1000.0); message.append(" seconds"); try { // JVM runtime double uptime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000.0; message.append(" (JVM running for ").append(uptime).append(")"); } catch (Throwable ex) { // No JVM time available } return message; } private void appendApplicationName(StringBuilder message) { // Get main class name String name = (this.sourceClass != null) ? ClassUtils.getShortName(this.sourceClass) : "application"; message.append(name); } }
the actual result is equivalent to
Started SpringbootApplication in 1.3 seconds (JVM running for 4.738)
4, Publish application context startup completion event
the specific path of the class where this method is located: org springframework. boot. SpringApplicationRunListeners
class SpringApplicationRunListeners { private final Log log; private final List<SpringApplicationRunListener> listeners; void started(ConfigurableApplicationContext context, Duration timeTaken) { doWithListeners("spring.boot.application.started", (listener) -> listener.started(context, timeTaken)); } private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) { doWithListeners(stepName, listenerAction, null); } private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) { StartupStep step = this.applicationStartup.start(stepName); this.listeners.forEach(listenerAction); if (stepAction != null) { stepAction.accept(step); } step.end(); } }
the specific path of the class where this method is located: org springframework. boot. context. event. EventPublishingRunListener
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { @Override public void started(ConfigurableApplicationContext context, Duration timeTaken) { // The context here is the annotation configservletwebserver ApplicationContext we obtained // Initialize ApplicationStartedEvent // Publishing events through application context ApplicationStartedEvent context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, timeTaken)); // Publish events through AvailabilityChangeEvent AvailabilityChangeEvent.publish(context, LivenessState.CORRECT); } }
4.1,ApplicationStartedEvent
When it comes to publishing this event, let's first look at a class diagram. We know that the context at this time is annotationconfigservletwebserver ApplicationContext
when the publishEvent method is called, it is also the parent class of the execution: org springframework. context. support. publishEvent method of abstractapplicationcontext
@Override public void publishEvent(ApplicationEvent event) { publishEvent(event, null); } protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); ApplicationEvent applicationEvent; // Determine whether event is an instance of ApplicationEvent if (event instanceof ApplicationEvent) { // Yes, turn to ApplicationEvent applicationEvent = (ApplicationEvent) event; } else { // No, it is decorated as applicationEvent through PayloadApplicationEvent applicationEvent = new PayloadApplicationEvent<>(this, event); if (eventType == null) { eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); } } // At this time, earlyApplicationEvents is null if (this.earlyApplicationEvents != null) { // Legacy events are not null this.earlyApplicationEvents.add(applicationEvent); } else { // If it is not null, broadcast the event through simpleapplicationeventmulticast getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Parent context is not empty if (this.parent != null) { // The parent context is an instance of AbstractApplicationContext if (this.parent instanceof AbstractApplicationContext) { // Convert to AbstractApplicationContext and publish the event ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { // Publish events directly using the parent context this.parent.publishEvent(event); } } }
what getapplicationeventmulticast() actually gets is simpleapplicationeventmulticast, which publishes ApplicationStartedEvent events. I believe those who read my article will be very familiar with the calling process. For details, please refer to: Alias interpretation of SpringBoot 2.6.0 source code (2): listener analysis of starting process analysis
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); // The executor defaults to null Executor executor = getTaskExecutor(); // 1. Get listener list based on event and event type // 2. Then traverse the listener list and call the listener methods respectively for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { // call invokeListener(listener, event); } } } }
finally, getapplicationlisters (event, type) obtains two listeners:
- BackgroundPreinitializer
- DelegatingApplicationListener
in fact, these two listeners do nothing
4.2,AvailabilityChangeEvent
public class AvailabilityChangeEvent<S extends AvailabilityState> extends PayloadApplicationEvent<S> { public static <S extends AvailabilityState> void publish(ApplicationContext context, S state) { Assert.notNull(context, "Context must not be null"); publish(context, context, state); } public static <S extends AvailabilityState> void publish(ApplicationEventPublisher publisher, Object source, S state) { Assert.notNull(publisher, "Publisher must not be null"); publisher.publishEvent(new AvailabilityChangeEvent<>(source, state)); } }
after the actual implementation, let's look at a simple class diagram:
in fact, when ApplicationEventPublisher calls the publishEvent method, it is AbstractApplicationContext to execute publishEvent. The process is the same as that in the above context. See the previous chapter for details. The listener results obtained here are as follows:
- DelegatingApplicationListener
- ApplicationAvailabilityBean
DelegatingApplicationListener does nothing here, and ApplicationAvailabilityBean maps:
Map<Class<? extends AvailabilityState>, AvailabilityChangeEvent<?>> events = new HashMap<>(); Added a pair of mappings
The key is org springframework. boot. availability. Livenessstate, value is org springframework. boot. availability. AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext]
5, Execute all Runner
the specific path of the class where this method is located: org springframework. boot. SpringApplication
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); } } }
actually, I haven't implemented anything here
6, Publishing ApplicationReadyEvent events
the specific path of the class where this method is located: org springframework. boot. SpringApplicationRunListeners
class SpringApplicationRunListeners { private final Log log; private final List<SpringApplicationRunListener> listeners; void ready(ConfigurableApplicationContext context, Duration timeTaken) { doWithListeners("spring.boot.application.ready", (listener) -> listener.ready(context, timeTaken)); } private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) { doWithListeners(stepName, listenerAction, null); } private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) { StartupStep step = this.applicationStartup.start(stepName); this.listeners.forEach(listenerAction); if (stepAction != null) { stepAction.accept(step); } step.end(); } }
the specific path of the class where this method is located: org springframework. boot. context. event. EventPublishingRunListener
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { @Override public void ready(ConfigurableApplicationContext context, Duration timeTaken) { // The context here is the annotation configservletwebserver ApplicationContext we obtained // Initialize ApplicationReadyEvent // Publish event ApplicationReadyEvent through application context context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, timeTaken)); // Publish events through AvailabilityChangeEvent AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC); } }
in fact, the calling process here is the same as the execution process of launching and completing the event before publishing the application context in this article. I won't say more. When publishing the event by the application context, the results obtained by the listener are as follows:
monitor | explain |
---|---|
SpringApplicationAdminMXBeanRegistrar | The ready traversal is set to true |
BackgroundPreinitializer | Wait for the pre initialization task to complete (when preparing the environment), and reduce the pre initialization complete to 0 |
DelegatingApplicationListener | Nothing was done |
publish the event through AvailabilityChangeEvent, and the results obtained from the listener are as follows:
monitor | explain |
---|---|
DelegatingApplicationListener | Nothing was done |
ApplicationAvailabilityBean | Refer to the results of chapter 4.2 in this paper |
epilogue
basically, the whole springboot startup process has been basically finished. We still have no explanation on the call of post processor, the execution of getBean method and the startup principle of main class. We look forward to your attention.