SpringBoot EventListener don't receive events - java

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.

Related

Spring boot actuator shutdown doesn't terminate infinite loop

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;
}
}

SpringBoot CommandLineRunner

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.

How to instantiate Spring boot Repository under standalone evironment?

all,
I am using spring boot in my project. It is great.
My project has a part of it that operates on database periodically(with a timer), and not in response to a http request.
It's periodically queries a sensor(a lot of sensors) and collects temperature readouts, and stores the readouts into database.
Before storing it into database, the readout number is compared to a warning threshold to test if a warning should be generated.
The threshold number is to be queried(complicated) out from database, too.
I have a ThresholdRepository extending JPAResository for this query, so I want to use it in this scenario.
My question is: Could I use #Autowire to make spring boot generate ThresholdRepository instance for me? If not, how to instantiate ThresholdRepository in this timer thread?
I find some code at :http://www.yjs001.cn/java/spring/33161827667841434606.html
unfortunately, the code is outdated and RepositoryMetadata has no getDomainClass and I don't know which alternative should be used.
Please someone help me out.
Any recommendation is appreciated.
The repository I mentioned is as following:
public interface ThresholdInfoRepository extends JpaRepository<ThresholdInfo, Long> {
ThresholdInfo findOneByGatewayIdAndNodeAddrAndChannel(Long gatewayId, Byte nodeAddr, Byte channel);
List<ThresholdInfo> findByGatewayId(Long gatewayId);
}
It's short, but does a lot of work.
Yes, you can,
You have to #EnableJpaRepositories for your repositories to become a bean.
Then, to be able to autowire it, your TimerTask needs to be a Spring Bean as well. You could use spring-tasks https://spring.io/guides/gs/scheduling-tasks/
#SpringBootApplication
#EnableScheduling
#EnableJpaRepositories
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class);
}
}
#Component
public class UpdateTask {
#Autowired
ThresholdInfoRepository thresholdInfoRepository;
#Scheduled(fixedRate = ...)
public void updateSensor() {
thresholdInfoRepository.find(...)
readoutRepository.save(...);
}
}
Spring boot will start a timer thread to execute your scheduled method.

How to run Spring Boot App with multiple CommandLineRunner

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.

Startup POJO On A Weld/Seam3 Application

I'm trying to get a POJO starting on startup within my Weld/Seam3 application but not having much luck. I've tried the following but none of them have worked:
#Singleton
public class StartupJobs {
#Inject
private Logger log;
public void onStartup(#Observes #Initialized ServletContextEvent event) {
log.info("Starting startup jobs");
}
public void onStartupTwo(#Observes #Initialized WebApplication webApplication) {
log.info("Starting startup jobs");
}
}
-
// Guessing this way is no good as I can't use the javax.ejb.Startup annotation here
#ApplicationScoped
public class StartupJobs {
#Inject
private Logger log;
#PostConstruct
public void onStartup() {
log.info("Starting startup jobs");
}
}
But neither of those ways worked. My log message was never raised. As this application is run on Tomcat6 and I've had to add the "org.jboss.weld.environment.servlet.Listener" listener to my web.xml, I'm wondering if there's something that class raises that I could observe. I didn't notice anything in particular though.
Any clue what else I could try?
Found out my issue was configuration. I hadn't seen I needed some extra configuration due to being on Tomcat 6: http://docs.jboss.org/seam/3/servlet/latest/reference/en-US/html/servlet-installation.html#installation.pre-servlet-3
A quick note on the documentation on that page as it stands as I write this, the class for the "Catch Exception Filter" should be "org.jboss.seam.servlet.exception.CatchExceptionFilter". The documentation is missing out the "exception". It seems to have been fixed in the Seam Servlet code so I imagine this bug will be fixed next time the documentation is released.

Categories