Reputation: 5557
I can't find a way to order the execution of post-init methods in Spring.
I need to order these methods because one is populating data used by the others init methods.
In the spring context I have a test component that is used only to init an in-memory db when it is needed. This component is defined in a dependency.
@Profile({"INMEMORY"})
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
public class InMemoryDatabaseInitializer {
@PostConstruct
private void setupInMemoryDb() {
// create db schema and populate it with test data into the inmemory db
}
}
I have a configuration object that is tested with this in memory profile and that perform a post construct operation that checks some flags in the db:
@Configuration
public class MyConfiguration {
@PostConstruct
private void checkDbFlags() {
// perform some SQL selects using JPA
}
}
With this setup, the MyConfiguration instance is resolved first and the post-init method checkDbFlags() is called before the in memory db is initialized by the post-init method setupInMemoryDb() and the test crashes.
I have also tried to move the db init method in the constructor this way:
@Profile({"INMEMORY"})
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
public class InMemoryDatabaseInitializer {
public InMemoryDatabaseInitializer() {
// perform some SQL selects using JPA
}
}
but even in this case, the Configuration post-init method checkDbFlags() is called first and crashs because the db structure is not yet installed.
I don't want to add a direct dependency between MyConfiguration and the InMemoryDatabaseInitializer, so I hoped that the @Order in front of the @Configuration would do the trick, but it doesn't. The issue occurs only when the post-init method for the db setup is located in another jar than the one using the db.
A demo application showing this behavior is available here: https://github.com/guame/so_spring_postinit_issue
You can run the com.example.demo.DemoApplicationTests to highlight the issue. Here is a sample log of the current execution:
2020-01-07 09:38:47.035 com.example.demo.DemoApplicationTests : Starting DemoApplicationTests
2020-01-07 09:38:47.036 com.example.demo.DemoApplicationTests : The following profiles are active: INMEMORY
2020-01-07 09:38:47.410 com.example.demo.DbInitializerFromApp : add DATA_FROM_APP into db
2020-01-07 09:38:47.415 yConfig$$EnhancerBySpringCGLIB$$1806b9e8 : checking the flags
2020-01-07 09:38:47.415 yConfig$$EnhancerBySpringCGLIB$$1806b9e8 : checkFlag from current app: DATA_FROM_APP
2020-01-07 09:38:47.415 yConfig$$EnhancerBySpringCGLIB$$1806b9e8 : checkFlag from dependencies jar: null
2020-01-07 09:38:47.418 c.example.demodata.DbInitializerFromJar : add DATA_FROM_DEPENDENCY into db
2020-01-07 09:38:47.476 com.example.demo.DemoApplicationTests : Started DemoApplicationTests in 0.802 seconds (JVM running for 1.942)
The flow is correct for the Initializer located in the application, but not for the Initializer located in a dependency.
How can I force the order of the post-init methods so the resolution of some of my initializing beans are resolved before the other even if they are located in dependencies ?
Upvotes: 0
Views: 265
Reputation: 165
I think the @DependsOn annotation might help in this case.
Upvotes: 0
Reputation: 2357
Using ApplicationListener or EventListener you can get notified when the complete Spring context has been initialized. If it's sufficent call your checkDbFlags()
method from there...
@Component
public class StartupApplicationListenerExample implements
ApplicationListener<ContextRefreshedEvent> {
@Override public void onApplicationEvent(ContextRefreshedEvent event) {
checkDbFlags();
}
}
https://www.baeldung.com/running-setup-logic-on-startup-in-spring
Upvotes: 1