New posts for Spring and Hibernate

This commit is contained in:
2026-11-06 01:05:27 +05:30
parent 51913ad9d4
commit dc13293046
136 changed files with 5268 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
# HelloApp started with: --greeting=hi --verbose notes.txt
hello: CommandLineRunner args = [--greeting=hi, --verbose, notes.txt]
hello: ApplicationRunner options = [greeting, verbose], non-option args = [notes.txt]
hello: ApplicationReadyEvent listener
process exit code: 0
@@ -0,0 +1,29 @@
# Every startup hook in the order Spring Boot 4.1.2 really calls it
--- non-web application ---
@PostConstruct
InitializingBean.afterPropertiesSet
SmartInitializingSingleton.afterSingletonsInstantiated
SmartLifecycle.start (default phase)
ContextRefreshedEvent
ApplicationStartedEvent
AvailabilityChangeEvent -> CORRECT
CommandLineRunner (readiness seen from inside: REFUSING_TRAFFIC, liveness: CORRECT)
ApplicationRunner (readiness seen from inside: REFUSING_TRAFFIC)
ApplicationReadyEvent (readiness seen from inside: REFUSING_TRAFFIC)
AvailabilityChangeEvent -> ACCEPTING_TRAFFIC
--- servlet web application (Tomcat on a random port) ---
@PostConstruct
InitializingBean.afterPropertiesSet
SmartInitializingSingleton.afterSingletonsInstantiated
WebServerInitializedEvent (port is open)
SmartLifecycle.start (default phase)
ContextRefreshedEvent
ApplicationStartedEvent
AvailabilityChangeEvent -> CORRECT
CommandLineRunner (readiness seen from inside: REFUSING_TRAFFIC, liveness: CORRECT)
ApplicationRunner (readiness seen from inside: REFUSING_TRAFFIC)
ApplicationReadyEvent (readiness seen from inside: REFUSING_TRAFFIC)
AvailabilityChangeEvent -> ACCEPTING_TRAFFIC
@@ -0,0 +1,12 @@
# CommandLineRunner and ApplicationRunner beans sorted together by @Order / Ordered
(declaration order in OrderingApp.java is NOT the order below)
CommandLineRunner @Order(HIGHEST_PRECEDENCE)
ApplicationRunner @Order(1) on the class
CommandLineRunner @Order(2) on the class
ApplicationRunner implements Ordered, getOrder() = 3
Both @Order(4) -> ApplicationRunner.run
Both @Order(4) -> CommandLineRunner.run
CommandLineRunner @Order(5) on a @Bean method
ApplicationRunner no order at all (lowest precedence)
@@ -0,0 +1,16 @@
# Four runners without @Order: the order chosen by each of 8 fresh JVMs
JVM 1: arD arB cliC cliA
JVM 2: cliC cliA arD arB
JVM 3: cliA arB arD cliC
JVM 4: arD arB cliC cliA
JVM 5: arD arB cliC cliA
JVM 6: cliC cliA arB arD
JVM 7: cliC cliA arB arD
JVM 8: arD arB cliC cliA
distinct sequences seen: 4
arD arB cliC cliA x4
cliC cliA arD arB x1
cliA arB arD cliC x1
cliC cliA arB arD x2
+14
View File
@@ -0,0 +1,14 @@
# The same command line, seen by CommandLineRunner and ApplicationRunner
args passed to SpringApplication.run: [--name=Ankur, --verbose, --name=Second, data.csv, -x, --, --after-the-dashes]
CommandLineRunner String[] args = [--name=Ankur, --verbose, --name=Second, data.csv, -x, --, --after-the-dashes]
ApplicationRunner getSourceArgs() = [--name=Ankur, --verbose, --name=Second, data.csv, -x, --, --after-the-dashes]
ApplicationRunner getOptionNames() = [name, verbose]
ApplicationRunner getOptionValues(name) = [Ankur, Second]
ApplicationRunner getOptionValues(verbose) = []
ApplicationRunner getOptionValues(missing) = null
ApplicationRunner containsOption(verbose) = true
ApplicationRunner getNonOptionArgs() = [data.csv, -x, --after-the-dashes]
Environment property 'name' (both values, joined by a comma): Ankur,Second
@@ -0,0 +1,23 @@
# A runner throws: exit code, log, events, and the runners that never ran
--- three ordered runners, the second throws (non-web, in a separate JVM) ---
TRACE ApplicationStartedEvent
TRACE runner 1 ran
TRACE runner 2 is about to throw
TRACE ApplicationFailedEvent: java.lang.IllegalStateException: boom from runner 2
TRACE ContextClosedEvent (the context is shutting down)
ERROR o.s.b.SpringApplication : Application run failed
java.lang.IllegalStateException: boom from runner 2
process exit code: 1
--- a runner exception that implements ExitCodeGenerator ---
ERROR o.s.b.SpringApplication : Application run failed
com.ankurm.runners.failure.FailureApp$ExitWith42Exception: the data file is missing
process exit code: 42
--- a runner that throws a checked exception (declared with throws Exception) ---
ERROR o.s.b.SpringApplication : Application run failed
java.lang.IllegalStateException: Failed to execute CommandLineRunner
Caused by: java.io.IOException: cannot read data.csv
process exit code: 1
@@ -0,0 +1,20 @@
# An ApplicationReadyEvent listener throws: the app was 'ready' and is now gone
--- non-web application ---
TRACE ApplicationStartedEvent
TRACE ready listener 1 is about to throw (readiness: REFUSING_TRAFFIC)
TRACE ContextClosedEvent (the context is shutting down)
ERROR o.s.b.SpringApplication : Application run failed
java.lang.IllegalStateException: boom from the ready listener
process exit code: 1
--- web application (Tomcat is up when the listener runs) ---
TRACE ApplicationStartedEvent
TRACE GET /ping from inside the listener -> 200 pong
TRACE ready listener 1 is about to throw (readiness: REFUSING_TRAFFIC)
TRACE ContextClosedEvent (the context is shutting down)
ERROR o.s.b.SpringApplication : Application run failed
java.lang.IllegalStateException: boom from the ready listener
process exit code: 1
does the port answer after the process ended? false
@@ -0,0 +1,16 @@
# A runner that blocks: the port is open, liveness is UP, readiness is OUT_OF_SERVICE
--- while the runner is blocked ---
has SpringApplication.run() returned? false
GET /ping -> 200 pong
GET /actuator/health/liveness -> 200 {"status":"UP"}
GET /actuator/health/readiness -> 503 {"status":"OUT_OF_SERVICE"}
--- after the runner returned ---
has SpringApplication.run() returned? true
GET /actuator/health/readiness -> 200 {"status":"UP"}
--- trace ---
runner: started on platform thread app-main, now waiting (think: warming a cache for 40 s)
runner: finished, returning to SpringApplication.run
@@ -0,0 +1,14 @@
# A runner that starts a virtual thread and returns: readiness does not wait, and failures do not fail startup
--- the background work is still waiting ---
has SpringApplication.run() returned? true
GET /actuator/health/readiness -> 200 {"status":"UP"}
--- after the background work threw ---
GET /actuator/health/readiness -> 200 {"status":"UP"}
GET /ping -> 200 pong
--- trace ---
runner: started the work on a virtual thread and returned
background work on a virtual thread failed: the warm-up blew up
@@ -0,0 +1,22 @@
# SmartLifecycle: start in ascending phase, stop in descending phase, runners after all of it
--- startup ---
start low (phase -100) on main
start mid (phase 0) on main
start high (phase 100) on main
start slow (phase 200) on main
ApplicationStartedEvent
CommandLineRunner runs
ApplicationReadyEvent
manual.isAutoStartup()=false isRunning()=false (never started by the context)
--- context.close() ---
start manual (phase 50) on main
ContextClosedEvent (stop phase begins)
stop slow (phase 200) begins, will finish in 300 ms
stop slow finished, calling the callback
stop high (phase 100) on main
stop manual (phase 50) on main
stop mid (phase 0) on main
stop low (phase -100) on main
@@ -0,0 +1,15 @@
# Every SmartLifecycle bean in a servlet web application, by phase (the ones this project adds are marked)
phase -2147483647 springBootLoggingLifecycle Lifecycle
phase -100 low (this project)
phase 0 mid (this project)
phase 50 manual (this project)
phase 100 high (this project)
phase 200 slow (this project)
phase 1073741823 applicationTaskExecutor ThreadPoolTaskExecutor
phase 2147481599 webServerStartStop WebServerStartStopLifecycle
phase 2147482623 webServerGracefulShutdown WebServerGracefulShutdownLifecycle
SmartLifecycle.DEFAULT_PHASE = 2147483647
Integer.MAX_VALUE = 2147483647
timeout per shutdown phase = 30000 ms (spring.lifecycle.timeout-per-shutdown-phase, set by Boot)
Spring Boot 4.1.2, Spring Framework 7.0.10
@@ -0,0 +1,9 @@
# SmartLifecycle.start() throws: no ApplicationStartedEvent, no runner
start healthy (phase 0)
start broken (phase 10)
stop healthy
application started? false
org.springframework.context.ApplicationContextException: Failed to start bean 'broken'
java.lang.IllegalStateException: could not connect to the broker
@@ -0,0 +1,6 @@
# isRunning() always false: start() is called, stop() never is
--- startup ---
start() was called
--- after context.close() ---
(nothing)
@@ -0,0 +1,6 @@
# @SpringBootTest(classes = SlicesApp.class, args = "--from-the-test=1"): what ran before the first test method
CommandLineRunner from a top-level @Component ran, args = [--from-the-test=1]
CommandLineRunner nested in SlicesApp ran, args = [--from-the-test=1]
ApplicationRunner from a @Bean method ran, source args = [--from-the-test=1]
ApplicationReadyEvent listener (a top-level @Component) ran
@@ -0,0 +1,4 @@
# @WebMvcTest(controllers = Hello.class): what ran before the first test method
CommandLineRunner nested in SlicesApp ran, args = []
ApplicationRunner from a @Bean method ran, source args = []
@@ -0,0 +1,81 @@
# Spring Boot 4.1.2 sources: the order SpringApplication.run() calls things in, and callRunners
--- SpringApplication.run(): from refresh to ready (lines containing the calls) ---
18: refreshContext(context);
19: afterRefresh(context, applicationArguments);
24: listeners.started(context, timeTakenToStarted);
25: callRunners(context, applicationArguments);
28: throw handleRunFailure(context, ex, listeners);
32: listeners.ready(context, startup.ready());
36: throw handleRunFailure(context, ex, null);
--- SpringApplication.callRunners ---
private void callRunners(ConfigurableApplicationContext context, ApplicationArguments args) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] beanNames = beanFactory.getBeanNamesForType(Runner.class);
Map<Runner, String> instancesToBeanNames = new IdentityHashMap<>();
for (String beanName : beanNames) {
instancesToBeanNames.put(beanFactory.getBean(beanName, Runner.class), beanName);
}
Comparator<Object> comparator = getOrderComparator(beanFactory)
.withSourceProvider(new FactoryAwareOrderSourceProvider(beanFactory, instancesToBeanNames));
instancesToBeanNames.keySet().stream().sorted(comparator).forEach((runner) -> callRunner(runner, args));
}
--- SpringApplication.callRunner (what happens to a bean that is both kinds) ---
private void callRunner(Runner runner, ApplicationArguments args) {
if (runner instanceof ApplicationRunner) {
callRunner(ApplicationRunner.class, runner, (applicationRunner) -> applicationRunner.run(args));
}
if (runner instanceof CommandLineRunner) {
callRunner(CommandLineRunner.class, runner,
(commandLineRunner) -> commandLineRunner.run(args.getSourceArgs()));
}
}
--- SpringApplication.callRunner(Class, Runner, ThrowingConsumer) (where 'Failed to execute' comes from) ---
private <R extends Runner> void callRunner(Class<R> type, Runner runner, ThrowingConsumer<R> call) {
call.throwing(
(message, ex) -> new IllegalStateException("Failed to execute " + ClassUtils.getShortName(type), ex))
.accept((R) runner);
}
--- EventPublishingRunListener.started / ready ---
public void started(ConfigurableApplicationContext context, @Nullable Duration timeTaken) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, timeTaken));
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
public void ready(ConfigurableApplicationContext context, @Nullable Duration timeTaken) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, timeTaken));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
--- the marker interface that both runners extend ---
interface Runner {
public interface CommandLineRunner extends Runner {
public interface ApplicationRunner extends Runner {
--- the same method in Spring Boot 3.0.13 ---
private void callRunners(ApplicationContext context, ApplicationArguments args) {
context.getBeanProvider(Runner.class).orderedStream().forEach((runner) -> {
if (runner instanceof ApplicationRunner applicationRunner) {
callRunner(applicationRunner, args);
}
if (runner instanceof CommandLineRunner commandLineRunner) {
callRunner(commandLineRunner, args);
}
});
}
--- the same method in Spring Boot 3.1.12 ---
private void callRunners(ConfigurableApplicationContext context, ApplicationArguments args) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] beanNames = beanFactory.getBeanNamesForType(Runner.class);
Map<Runner, String> instancesToBeanNames = new IdentityHashMap<>();
for (String beanName : beanNames) {
instancesToBeanNames.put(beanFactory.getBean(beanName, Runner.class), beanName);
}
Comparator<Object> comparator = getOrderComparator(beanFactory)
.withSourceProvider(new FactoryAwareOrderSourceProvider(beanFactory, instancesToBeanNames));
instancesToBeanNames.keySet().stream().sorted(comparator).forEach((runner) -> callRunner(runner, args));
}
@@ -0,0 +1,31 @@
# javap -p -c on spring-boot-4.1.2.jar: the compiled SpringApplication.callRunners
--- javap -p org.springframework.boot.Runner / CommandLineRunner / ApplicationRunner ---
Compiled from "Runner.java"
interface org.springframework.boot.Runner {
}
Compiled from "CommandLineRunner.java"
public interface org.springframework.boot.CommandLineRunner extends org.springframework.boot.Runner {
public abstract void run(java.lang.String...) throws java.lang.Exception;
}
Compiled from "ApplicationRunner.java"
public interface org.springframework.boot.ApplicationRunner extends org.springframework.boot.Runner {
public abstract void run(org.springframework.boot.ApplicationArguments) throws java.lang.Exception;
}
--- invoke* instructions inside callRunners ---
invokeinterface #393, 1 // InterfaceMethod org/springframework/context/ConfigurableApplicationContext.getBeanFactory:()Lorg/springframework/beans/factory/config/ConfigurableListableBeanFactory;
invokeinterface #966, 2 // InterfaceMethod org/springframework/beans/factory/config/ConfigurableListableBeanFactory.getBeanNamesForType:(Ljava/lang/Class;)[Ljava/lang/String;
new #970 // class java/util/IdentityHashMap
invokespecial #972 // Method java/util/IdentityHashMap."<init>":()V
invokeinterface #973, 3 // InterfaceMethod org/springframework/beans/factory/config/ConfigurableListableBeanFactory.getBean:(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;
invokeinterface #977, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
invokevirtual #983 // Method getOrderComparator:(Lorg/springframework/beans/factory/config/ConfigurableListableBeanFactory;)Lorg/springframework/core/OrderComparator;
new #987 // class org/springframework/boot/SpringApplication$FactoryAwareOrderSourceProvider
invokespecial #989 // Method org/springframework/boot/SpringApplication$FactoryAwareOrderSourceProvider."<init>":(Lorg/springframework/beans/factory/config/ConfigurableBeanFactory;Ljava/util/Map;)V
invokevirtual #992 // Method org/springframework/core/OrderComparator.withSourceProvider:(Lorg/springframework/core/OrderComparator$OrderSourceProvider;)Ljava/util/Comparator;
invokeinterface #998, 1 // InterfaceMethod java/util/Map.keySet:()Ljava/util/Set;
invokeinterface #1001, 1 // InterfaceMethod java/util/Set.stream:()Ljava/util/stream/Stream;
invokeinterface #1002, 2 // InterfaceMethod java/util/stream/Stream.sorted:(Ljava/util/Comparator;)Ljava/util/stream/Stream;
invokedynamic #1006, 0 // InvokeDynamic #12:accept:(Lorg/springframework/boot/SpringApplication;Lorg/springframework/boot/ApplicationArguments;)Ljava/util/function/Consumer;
invokeinterface #1009, 2 // InterfaceMethod java/util/stream/Stream.forEach:(Ljava/util/function/Consumer;)V
@@ -0,0 +1,97 @@
# spring-context 7.0.10 sources: DefaultLifecycleProcessor, the object that starts and stops SmartLifecycle beans
--- doStart: start() is called only when isRunning() is false ---
private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName,
boolean autoStartupOnly, @Nullable List<CompletableFuture<?>> futures) {
Lifecycle bean = lifecycleBeans.remove(beanName);
if (bean != null && bean != this) {
String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName);
for (String dependency : dependenciesForBean) {
doStart(lifecycleBeans, dependency, autoStartupOnly, futures);
}
if (!bean.isRunning() && (!autoStartupOnly || toBeStarted(beanName, bean))) {
if (futures != null) {
futures.add(CompletableFuture.runAsync(() -> doStart(beanName, bean), getBootstrapExecutor()));
}
else {
doStart(beanName, bean);
}
}
}
}
--- doStop: stop() is called only when isRunning() is true ---
private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, final String beanName,
boolean pauseableOnly, final CountDownLatch latch, final Set<String> countDownBeanNames) {
Lifecycle bean = lifecycleBeans.remove(beanName);
if (bean != null) {
String[] dependentBeans = getBeanFactory().getDependentBeans(beanName);
for (String dependentBean : dependentBeans) {
doStop(lifecycleBeans, dependentBean, pauseableOnly, latch, countDownBeanNames);
}
try {
if (bean.isRunning()) {
Set<String> stoppedBeans = this.stoppedBeans;
if (stoppedBeans != null) {
stoppedBeans.add(beanName);
}
if (bean instanceof SmartLifecycle smartLifecycle) {
if (!pauseableOnly || smartLifecycle.isPauseable()) {
if (logger.isTraceEnabled()) {
logger.trace("Asking bean '" + beanName + "' of type [" +
bean.getClass().getName() + "] to stop");
}
countDownBeanNames.add(beanName);
smartLifecycle.stop(() -> {
latch.countDown();
countDownBeanNames.remove(beanName);
if (logger.isDebugEnabled()) {
logger.debug("Bean '" + beanName + "' completed its stop procedure");
}
});
}
else {
// Don't wait for beans that aren't pauseable...
latch.countDown();
}
}
else if (!pauseableOnly) {
if (logger.isTraceEnabled()) {
logger.trace("Stopping bean '" + beanName + "' of type [" +
bean.getClass().getName() + "]");
}
bean.stop();
if (logger.isDebugEnabled()) {
logger.debug("Successfully stopped bean '" + beanName + "'");
}
}
}
else if (bean instanceof SmartLifecycle) {
// Don't wait for beans that aren't running...
latch.countDown();
}
}
catch (Throwable ex) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to stop bean '" + beanName + "'", ex);
}
if (bean instanceof SmartLifecycle) {
latch.countDown();
}
}
}
}
--- isAutoStartupCandidate / toBeStarted: what isAutoStartup() decides ---
private boolean isAutoStartupCandidate(String beanName, Lifecycle bean) {
Set<String> stoppedBeans = this.stoppedBeans;
return (stoppedBeans != null ? stoppedBeans.contains(beanName) :
(bean instanceof SmartLifecycle smartLifecycle && smartLifecycle.isAutoStartup()));
}
private boolean toBeStarted(String beanName, Lifecycle bean) {
Set<String> stoppedBeans = this.stoppedBeans;
return (stoppedBeans != null ? stoppedBeans.contains(beanName) :
(!(bean instanceof SmartLifecycle smartLifecycle) || smartLifecycle.isAutoStartup()));
}