#!/usr/bin/env bash # Facts that come from the shipped jars and their sources rather than from running the scenarios: # 15 SpringApplication.run / callRunners / EventPublishingRunListener, read from the 4.1.2 sources jar, # and the same method in Boot 3.0.13 and 3.1.12 for contrast # 16 the same method read out of the compiled 4.1.2 class with javap (what it really calls) # 17 DefaultLifecycleProcessor: when start() and stop() are called set -euo pipefail cd "$(dirname "$0")/.." mkdir -p output target/facts BOOT=4.1.2 SPRING=7.0.10 M2=~/.m2/repository fetch() { # url, destination: Maven Central rate-limits shared addresses, so retry politely for i in 1 2 3 4 5 6; do code=$(curl -s -o "$2" -w '%{http_code}' "$1" || true) [ "$code" = 200 ] && return 0 sleep $((i * 10)) done echo "could not fetch $1 (last status $code)" >&2; return 1 } src() { # groupPath artifact version local f="target/facts/$2-$3-sources.jar" [ -s "$f" ] || fetch "https://repo1.maven.org/maven2/$1/$2/$3/$2-$3-sources.jar" "$f" echo "$f" } method() { # sources-jar file-in-jar start-regex -> prints from the first match to the first line that is a single tab + } unzip -p "$1" "$2" | sed -n "/$3/,/^ }/p" } BOOT_SRC=$(src org/springframework/boot spring-boot $BOOT) OLD0=$(src org/springframework/boot spring-boot 3.0.13) OLD1=$(src org/springframework/boot spring-boot 3.1.12) FW_SRC=$(src org/springframework spring-context $SPRING) APP=org/springframework/boot/SpringApplication.java { echo "# Spring Boot $BOOT sources: the order SpringApplication.run() calls things in, and callRunners" echo echo "--- SpringApplication.run(): from refresh to ready (lines containing the calls) ---" unzip -p "$BOOT_SRC" $APP | sed -n '/public ConfigurableApplicationContext run(String... args)/,/^ }/p' \ | grep -n -E 'refreshContext|afterRefresh|listeners\.(started|ready)|callRunners|handleRunFailure' | sed 's/^\([0-9]*\):\s*/\1: /' echo echo "--- SpringApplication.callRunners ---" method "$BOOT_SRC" $APP 'private void callRunners' echo echo "--- SpringApplication.callRunner (what happens to a bean that is both kinds) ---" method "$BOOT_SRC" $APP 'private void callRunner(Runner runner' echo echo "--- SpringApplication.callRunner(Class, Runner, ThrowingConsumer) (where 'Failed to execute' comes from) ---" method "$BOOT_SRC" $APP 'private void callRunner' echo echo "--- EventPublishingRunListener.started / ready ---" method "$BOOT_SRC" org/springframework/boot/context/event/EventPublishingRunListener.java 'public void started' method "$BOOT_SRC" org/springframework/boot/context/event/EventPublishingRunListener.java 'public void ready' echo echo "--- the marker interface that both runners extend ---" unzip -p "$BOOT_SRC" org/springframework/boot/Runner.java | grep -E '^(interface|public interface)' unzip -p "$BOOT_SRC" org/springframework/boot/CommandLineRunner.java | grep -E '^public interface' unzip -p "$BOOT_SRC" org/springframework/boot/ApplicationRunner.java | grep -E '^public interface' echo echo "--- the same method in Spring Boot 3.0.13 ---" method "$OLD0" $APP 'private void callRunners' echo echo "--- the same method in Spring Boot 3.1.12 ---" method "$OLD1" $APP 'private void callRunners' } > output/15-callrunners-source.txt JAR=$(find "$M2/org/springframework/boot/spring-boot/$BOOT" -name "spring-boot-$BOOT.jar" | head -1) { echo "# javap -p -c on spring-boot-$BOOT.jar: the compiled SpringApplication.callRunners" echo echo "--- javap -p org.springframework.boot.Runner / CommandLineRunner / ApplicationRunner ---" javap -p -cp "$JAR" org.springframework.boot.Runner org.springframework.boot.CommandLineRunner org.springframework.boot.ApplicationRunner echo echo "--- invoke* instructions inside callRunners ---" javap -p -c -cp "$JAR" org.springframework.boot.SpringApplication \ | sed -n '/private void callRunners(/,/^$/p' | grep -E 'invoke|new ' | sed 's/^ *[0-9]*: //' } > output/16-javap-callrunners.txt CTX=$(find "$M2/org/springframework/spring-context/$SPRING" -name "spring-context-$SPRING.jar" | head -1) LP=org/springframework/context/support/DefaultLifecycleProcessor.java { echo "# spring-context $SPRING sources: DefaultLifecycleProcessor, the object that starts and stops SmartLifecycle beans" echo echo "--- doStart: start() is called only when isRunning() is false ---" method "$FW_SRC" $LP 'private void doStart(Map' echo echo "--- doStop: stop() is called only when isRunning() is true ---" method "$FW_SRC" $LP 'private void doStop(Map' echo echo "--- isAutoStartupCandidate / toBeStarted: what isAutoStartup() decides ---" method "$FW_SRC" $LP 'private boolean isAutoStartupCandidate' method "$FW_SRC" $LP 'private boolean toBeStarted' } > output/17-lifecycleprocessor-source.txt ls -1 output | tail -3