I have a process which is an infinite loop:
public class MyProcess {
public void start() {
while (true) {
//process
}
}
}
When I started using Spring Boot, my first approach was to get the bean from the context after the application had started and manually start the process:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(MyApplication.class, args);
MyProcess myProcess = applicationContext.getBean(MyProcess.class);
myProcess.start();
}
}
This worked but did not seem like the right way of doing it, so I implemented a CommandLineRunner:
#Component
public class MyRunner implements CommandLineRunner {
#Autowired
private MyProcess myProcess;
#Override
public void run(String... args) {
this.myProcess.start();
}
}
This is almost the same as before, but here the SpringApplication#run call never actually finishes, which seems also wrong. Am I correct to think that?
I then tried to manage this on my own with an Executor, but in order to catch any Exception I had to call Future#get which resulted in the same blocking state.
Instead of this, I am now using an #Async annotation on the CommandLineRunner#run method.
This seems to be the best solution as the application finishes starting after the task has also been started. Any Exception will be picked up and logged, but the backing Executor created by Sprint prevents the application from shutting down.
I assume there is a way of asking Spring to terminate the Executor so that the entire application can exit since I am managing it externally with Supervisor.
Is this the correct approach or am I missing a step? Should the application be independent and restart the process after a failure?
Do some work in the main thread. What is wrong with calling myProcess.start() from main() without #Async? When you finish your job just leave loop in MyProcess#start
Related
In Sprint Boot 2.x we can initialize our application in one of 2 approaches:
#SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
init();
}
private void init() {
// Init the app from in here...
}
}
Or we can use a startup listener that will execute on startup:
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// Init the app from in here...
}
}
I'm wondering what tradeoffs exist by taking either approach. Not knowing much about Spring Boot's "app lifecycle", I'm wondering if there are things I will/won't have access to in either setup. Thanks in advance!
The init method is only called after startup, and is only called when running your application as a command-line program.
The init method is e.g. not called when deploying your application as a .war file.
The onApplicationEvent method is called whenever a ContextRefreshedEvent is fired, which does happen during startup, but can be called again later. See e.g. "When is ContextRefreshedEvent fired in Spring?"
For a more comparable event to the init method, use ApplicationStartedEvent.
Im my spring boot application I have a component with a method that run some job in the infinite loop below, actually it checks some data in db:
while(true) {// DOES SOME JOB}
here is my app entry point for spring boot app:
#SpringBootApplication
public class Application implements CommandLineRunner {
private final Service service;
#Autowired
public Application(Service service) {
this.service = service;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... args) {
service.run();
}
}
and I enabled actuator's shutdown endpoint, so I would kill the app via:curl -X POST localhost:8080/actuator/shutdown and in my case it kills only spring context but the loop still run... Is it possible to kill the whole app like System.exit(0) does (even with infinite loop).
NOTE: I know it's possible to write my own context aware endpoint with shutdown link and whenever some one request the endpoint I can close the spring context and Syste.exit(0) (it will definitely terminate the llop) and even provide boolean flag to the infinite loop, but does the spring provide something by default, let's say more elegant?
You can gracefully shutdown your application using #PreDestroy annotation
#PreDestroy
public void destroy() {
System.out.println("destroy");
}
So, when you use ctrl+C to kill your application, the loop will also be killed.
As for the actuator, its "shutdown" method closes the application context.
Although the bean is managed by spring but since there is an infinite loop in some of its methods, spring can't really know this and can't really break this loop.
Spring does manage the life-cycle of the beans and indeed like #Shruti Gupta has stated, you can create a method annotated with #PreDestroy annotation so that spring will call it, but then again, you must implement the logic of breaking the loop.
Here is an example of something that might work for you:
#Component
public class MyBean {
private boolean shouldKeepCheckingDB = true; // consider volatile as well if you need it, kind of out of scope for this question, but might be useful
public void checkDB() {
while(shouldKeepCheckingDB) { // instead of while(true)
// check the database
}
}
#PreDestroy
void stop() {
this.shouldKeepCheckingDB = false;
}
}
In what circumstances CommandLineRunner is preferred instead of writing additional code in the main method of SpringBoot application.
I understand that CommandLineRunner gets executed before main gets completed.
In simple cases, there is no difference.
But if the code need to access features provided by spring, such as ioc or only interface repositories/services, you need to wait for the complete application startup. And the call of the overrided run method after completion is garanteed.
Besides, CommandLineRunner has other advantages:
Can be implemented multiple times
The capability to start any scheduler or log any message before application starts to run
I have used it to decouple code. Instead of placing a bunch of code into main method, the CommandLineRunner lets you distribute it more evenly around the codebase. It really depends on what kind of flags you are passing in and why you need to pass them in. Spring offers a lot of flexibility for you to get the job done in the easiest way.
For a full on command line tool, you can decouple initialization and config a little bit by dividing your code between init and core behavior.
A spring boot server can overwrite configuration based on args passed in from the command line.
I would suggest all time time. It adds a lot of flexibility to your "bootstrapping code".
1) For example, command line runners are spring #Beans, so you can activate/deactivate them at run-time.
2) You can use them in an ordered fashion by appending the #Order annotation
3) You can unit test them just like regular classes
4) You can inject dependencies into them. Each runner can then have its own dependencies.
All of the above are more difficult, if not impossible to achieve if you add all your bootstrapping logic in the main() method of the Spring Application class.
Hope my answer helps,
Cheers
I haven't found any good reason for using it over just writing code after starting the application.
The only thing I can think of is that the command line runners are called before any SpringApplicationRunListeners have #finished called.
I have seen people use them to perform main application logic, but I think this is an antipattern.
One of the annoying things about doing so is that the application start timer is still running and when the task completes you will see a log entry like Started DemoApplication in 5.626 seconds (JVM running for 0.968).
It's confusing to see a message about your application starting despite, in reality it having just finished.
I encountered a scenario where i had to keep a certain data from the db loaded into the cache before the method was hit from the controller end point the first time. In this scenario it was desirable to hit the method for populating the cache using the run method after extending CommandLineRunner class so that before the application even starts up the data is already available in the cache.
I use this to populate my Default Data. I usually create ApplicationInitializer class that extends CommandLineRunner.
I have methods like createDefaultUser(), createDefaultSytemData() etc.
This way I do not rely on sql files to populate database for me. :)
ApplicationRunner and CommandLineRunner:
two of them can execute some custom code before your application finished starting up.
ComandLineRunner:
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(CommandLineAppStartupRunner.class);
#Override
public void run(String...args) throws Exception {
logger.info(Arrays.toString(args));
}
}
you can get the args directly
ApplicationRunner:
#Component
public class AppStartupRunner implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(AppStartupRunner.class);
#Override
public void run(ApplicationArguments args) throws Exception {
logger.info(args.getOptionNames());
}
}
ApplicationRunner has many methods to get params
ComandLineRunner can get params directly
if you custom two runner class, you can use annotation #Order to Specify the order of execution
public class Phone {
#Autowired
BeanExample beanExample;
public void print(){
beanExample.fn();
}
}
public class BeansCreatorClass {
#Bean
public BeanExample getBeanExample(){
return new BeanExample();
}
#Bean
public Phone getPhone(){
return new Phone();
}
}
#SpringBootApplication
public class SpringBootRunnerConfigurationPropertiesApplication implements CommandLineRunner, ApplicationRunner {
public static void main(String[] args){
SpringApplication.run(SpringBootRunnerConfigurationPropertiesApplication.class, args);
System.out.println("==== spring boot commandLine is running === ");
// beans creator class is the class contains all beans needed
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeansCreatorClass.class);
Phone phone = applicationContext.getBean(Phone.class);
phone.print();
}
// commandLineRunner
public void run(String... args) throws Exception {
System.out.println("=== commandLine Runner is here ==== ");
}
// application runner
#Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("=== application runner is here ====");
}
}
I mostly use CommandLineRunner to:
Apply initial migrations
Run a code that is independent of REST/SOAP calls.
I am trying to run a Spring Boot Application with multiple CommandLineRunner implementations in hope, that all run methods will be started.
But it is only one of them, anyway both Implementations are created.
First:
#Component
public class TestRunnerA implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
logger.info("starting: TestRunnerA");
consume();
}
}
Second:
#Component
public class TestRunnerB implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
logger.info("starting: TestRunnerB");
consume();
}
}
In this case, only the Run() Method of TestRunnerA ist called.
Does somebody know why?
I tried adding a #Order annotation, but still... (the first in the order is called)
Kind regards,
Knut
You can annotate different runners with different spring profiles and specify required profile in your launching script using:
-Dspring.profiles.active=YourProfile
You could make a single SpringBootApplication that implements CommandLineRunner then inject all the other runners as #Component and use the first argument to delegate which command should take the rest of the arguments. Here is an example in my answer here: https://stackoverflow.com/a/58777948/986160
Important: be careful though if you put it in a crontab one call will destroy the previous one if it is not done.
I'm starting to learn Java with Spring and I've written some simple scheduled tasks.
I don't understand the mechanism that the framework uses so the application does not exit after the getBean call. How come the application keeps on printing "Hi"?
public class Application {
public static void main(String[] args) {
...
PeriodicTask task = appCtx.getBean(PeriodicTask.class);
}
}
public class PeriodicTask {
#Scheduled(fixedRate = 5000)
public void periodic() {
System.out.println("Hi");
}
}
Given #Scheduled, I'm going to assume your ApplicationContext has some scheduled configuration. This means that your are creating (implicitly or explicitly) a SchedulerExecutorService which spawns non-daemon threads. The application will not end until all non-daemon threads have completed. One of these threads is executing your periodic method every 5000 milliseconds.
Now, you could put your ApplicationContext instantiation in a try-with-resources. Once execution leaves the try block, the ApplicationContext would be closed, shutting down the ScheduledExecutorService and eventually terminating your program.