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;
}
}
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.
I am trying to find a way to have a graceful Runnable bean creation in Spring boot. The point of the app is to have a service, which would take in some data, and start a monitored external process.
In my previous attempts I simply formed a regular new MyRunnable() and passed it to the execution service. Now I'm thinking how to properly do so using the Spring environment, and use the #Scope("prototype").
I did find examples, which used ApplicationContext.getBean(...), and a better approach of Why is Spring's ApplicationContext.getBean considered bad?, but I still fail to properly digest how to actually call the new MyRunnable() in terms of one service, which would follow the simple idea of:
class MyService {
public void triggerNewExternalTask() {
....
executionService.run(new MyRunnable());
I believe you're on the wrong path here.
Spring dependency injection is wonderful, but that does not mean that you'll never find a call to new in a properly written Spring Boot app.
This is a case where calling new is the right thing to do. Every Executor in the pool should get its own instance of Runnable/Callable when it starts.
This is true for any method scoped variable: better to instantiate it in method scope and let the garbage collector clean it up when you exit the method. No reason for Spring to be responsible for the bean life cycle in that case.
You go too far when you try to share Runnable instances, especially if they have state.
Even as the question is closed, stumbled over another solution, which is - #Lookup, which meets the task:
entity:
#Component
#Scope("prototype")
public class Proto {
private static int counter;
public Proto() {
System.out.println("count: "+counter++);
}
}
service:
#Service
public class ProtoService {
#Lookup
public Proto getProto() {
return null;
}
}
and the test:
#Service
public class LookupWorks {
#Autowired
private ProtoService serv;
#PostConstruct
private void test() {
System.out.println(">>>>>>>>>>>>>>");
serv.getProto();
serv.getProto();
serv.getProto();
serv.getProto();
serv.getProto();
System.out.println(">>>>>>>>>>>>>>");
}
}
with the output:
>>>>>>>>>>>>>>
count: 0
count: 1
count: 2
count: 3
count: 4
>>>>>>>>>>>>>>
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 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
my EventListener annotation don't receive any Spring Event. Here is my code:
#Component
public class ProxyConfig {
public ProxyConfig() {
System.out.println("I can see this in the console");
}
#EventListener
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
System.out.println("WON'T WORK :-("); // FIXME
}
#EventListener
public void test(ApplicationStartedEvent event) {
System.out.println("WON'T WORK :-("); // FIXME
}
}
And here is my Application class:
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApp.class, args);
}
}
According to https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2 and https://solidsoft.wordpress.com/2015/09/29/annotation-driven-event-listeners-in-spring-4-2/ it must be working, but it still not print my "WON'T WORK :-(" String :(
Any idea?
Thanks!
The two events that you are listening for are both published very early in an application's lifecycle.
ApplicationStartedEvent is sent "as early as conceivably possible as soon as a SpringApplication has been started - before the Environment or ApplicationContext is available, but after the ApplicationListeners have been registered".
ApplicationEnvironmentPreparedEvent is published "when a SpringApplication is starting up and the Environment is first available for inspection and modification."
In both cases, the event is published too early for a listener to be found via annotations and the application context. As you've observed you can use spring.factories to register your listener. Alternatively, you can use the setter method on SpringApplication.
This is an old question, but anyway, to anyone having the same issue...
I've just wasted a few hours with the exact same problem, I've searched the internet and did countless tests to no avail, I've tried annotating with #EventListener(ApplicationReadyEvent.class) and #PostConstruct, nothing worked.
My philosophy is that when something simple doesn't work and the internet doesn't help, it means you messed up something. So I started reviewing my code and I see you've made the same mistake as me.
You've copied the example from somewhere else or changed the original configuration class from MyApp to Application, and you've forgotten to change the line:
SpringApplication.run(MyApp.class, args);
to
SpringApplication.run(Application.class, args);
The first line will do nothing, unless MyApp also has #SpringBootApplication or other configuration annotations, the second one will find the #SpringBootApplication annotation and will properly start the Spring Boot application.
By the way, both #EventListener(ApplicationReadyEvent.class) and #PostConstruct work just fine, tested on Java 17.