I'm a beginner and after searching as much as I could, I can't figure out how Spring decides it's finished starting up.
This may actually be a spring-shell specific question.
Spring shell startup looks like this (for example):
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.17)
2023-12-05 11:54:51.060 INFO 10364 --- [ main] c.e.training.ticketservice.Application : Starting Application using Java 21.0.1 on MYMACHINE with PID 10364 (D:\mypath\ticket-service\target\classes started by Lenovo in D:\proj\epam-deik-java-dev)
2023-12-05 11:54:51.063 INFO 10364 --- [ main] c.e.training.ticketservice.Application : The following 4 profiles are active: "dev", "h2db-dev", "debug", "debug-sql"
2023-12-05 11:54:51.661 INFO 10364 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2023-12-05 11:54:51.732 INFO 10364 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 60 ms. Found 1 JPA repository interfaces.
2023-12-05 11:54:52.379 INFO 10364 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2023-12-05 11:54:52.453 INFO 10364 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.6.15.Final
2023-12-05 11:54:52.702 INFO 10364 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2023-12-05 11:54:52.845 INFO 10364 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2023-12-05 11:54:57.759 INFO 10364 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2023-12-05 11:54:57.779 INFO 10364 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect:
2023-12-05 11:54:58.923 INFO 10364 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2023-12-05 11:54:58.939 INFO 10364 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2023-12-05 11:54:59.736 WARN 10364 --- [ main] org.jline : The Parser of class org.springframework.shell.jline.ExtendedDefaultParser does not support the CompletingParsedLine interface. Completion with escaped or quoted words won't work correctly.
2023-12-05 11:54:59.821 INFO 10364 --- [ main] c.e.training.ticketservice.Application : Started Application in 9.437 seconds (JVM running for 10.43)
shell:>
I would like to run some code after JPA has come up and created the database schemas, but before startup finishes and hands control to the user. I've managed to figure out that I probably need to use something like @DependsOn("entityManagerFactory")
to make sure my @Component
code
executes after JPA is finished (?), but I can't figure out how I would tell Spring that my @Component
is part of startup, and shouldn't hand control to the user till it's done.
I've tried looking through documentation: Lifecycle, SmartLifecycle, looking through https://docs.spring.io/spring-framework/reference/core/ , but I can't figure out how Spring knows what beans belong to startup, and when their startup code is finished executing.
More verbose output shows the following few lines before the prompt (among other things):
2023-12-05 12:03:28.047 INFO 27096 --- [ main] c.e.training.ticketservice.Application : Started Application in 10.331 seconds (JVM running for 11.311)
2023-12-05 12:03:28.049 DEBUG 27096 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state LivenessState changed to CORRECT
2023-12-05 12:03:28.050 DEBUG 27096 --- [ main] o.s.shell.DefaultShellApplicationRunner : Checking shell runners [org.springframework.shell.jline.ScriptShellRunner@3a7dcfb7, org.springframework.shell.jline.NonInteractiveShellRunner@1642968c, org.springframework.shell.jline.InteractiveShellRunner@50b624da]
2023-12-05 12:03:28.051 DEBUG 27096 --- [ main] o.s.shell.DefaultShellApplicationRunner : Using shell runner org.springframework.shell.jline.InteractiveShellRunner@50b624da
shell:>
The closest I've come is perhaps that event listeners are run synchronously by default, but maybe that's completely irrelevant.
Sorry for asking here, I don't use StackOverflow. It could be argued that this is a documentation bug. I hope it's not a big problem.
Comment From: snicoll
There is an entire section on the subject, how about you have a component that injects the EntityManager
and then performs something in it's afterPropertiesSet
method (or similar mechanism as described in that section)?
Sorry for asking here, I don't use StackOverflow.
Unfortunately, our policy is to ask questions on StackOverflow, and we can't really make exceptions based on your preferences. If the above doesn't help, I'd like to understand what's missing from the documentation.
Comment From: deliciouslytyped
Ok I got some sleep and have some energy and focus for deeper-diving again.
Thanks for the pointer! Under your direction I (re)discovered several things pointing in the right direction (more on this later), but it will take me some time to better understand this. In the meantime, the Spring core documentation seems to focus on the bean lifecycle side of things, - though several points hint at or explain things relating to the application lifecycle / applicationcontext side - do you have anything for the application lifecycle as opposed to the bean lifecycle? (This question makes sense right?) *
* However, I discovered that I'm probably fundamentally misunderstanding roles of components in and around the ecosystem.
It looks like what I'm looking for may actually be something Spring Boot does and not the core of the framework. For example, the Spring Boot documentation contains documentation on various events like: https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/event/ApplicationStartedEvent.html
(How does Spring know when to fire the ApplicationStartedEvent and where does it fire it from? This might be what causes 2023-12-05 11:54:59.821 INFO 10364 --- [ main] c.e.training.ticketservice.Application : Started Application in 9.437 seconds (JVM running for 10.43)
, and I can possibly work backwards from it. It might also be futile to look at the Spring source without conceptual understanding due to it's complexity.)
A lot of the things that are unclear currently can probably be figured out from the docs and code but I need to spend some more time on it.
Thanks.
Edit: Again, experience shows reading the source is probably a decent second resort if not first. I thought it would be more difficult to find what I needed because I wasn't sure where to get sources from, but IntelliJ was pretty helpful.
Following up on ApplicationStarted event, this file can be found by searching GitHub, as the only file visibly creating an instance: - https://github.com/spring-projects/spring-boot/blob/9eae176fce6f280a1ff8d96e2b912b55ef509c80/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/event/EventPublishingRunListener.java#L103 - that class implements started() in https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/SpringApplicationRunListener.html , which refers to https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/SpringApplication.html (I should have figured as much)
Following the thread of searching for usages of started() in IntelliJ leads down the path: - https://github.com/spring-projects/spring-boot/blob/9eae176fce6f280a1ff8d96e2b912b55ef509c80/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplicationRunListeners.java#L76 - https://github.com/spring-projects/spring-boot/blob/9eae176fce6f280a1ff8d96e2b912b55ef509c80/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java#L332
I expected a much bigger tangle of some kind of event based stuff, but the code here pretty clearly follows whats demonstrated in the log in the original post: https://github.com/spring-projects/spring-boot/blob/9eae176fce6f280a1ff8d96e2b912b55ef509c80/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java#L310-L333 (note the link highlights a section, just slow to load the big file.)
This is certainly progress so I'll leave it here for the moment and if anything is still problematic I have a thread to start following.
Comment From: snicoll
OK, sorry. But that's a lot of text and I skimmed through it and couldn't find something actionable. If you have, then please provide a summary of what you'd like to be changed and we can reopen this issue. Or you can create another or submit a PR to the documentation. Thanks.
Comment From: deliciouslytyped
The documentation says
Be aware that @PostConstruct and initialization methods in general are executed within the container’s singleton creation lock. The bean instance is only considered as fully initialized and ready to be published to others after returning from the @PostConstruct method. Such individual initialization methods are only meant for validating the configuration state and possibly preparing some data structures based on the given configuration but no further activity with external bean access. Otherwise there is a risk for an initialization deadlock.
This means that it's not appropriate to do any work inside a @PostConstruct
method, correct?