not able to publish custom event in spring before context load - java

I am trying to publish a custom event in Spring MVC, but is is not firing while context is loading, below are the code snippet,
The onConnectionOpened will be called after connecting to a server which is triggered after bean initialization using #PostConstruct
#Autowired
private ApplicationEventPublisher publisher;
public void onConnectionOpened(EventObject event) {
publisher.publishEvent(new StateEvent("ConnectionOpened", event));
}
I am using annotation in listener part as below
#EventListener
public void handleConnectionState(StateEvent event) {
System.out.println(event);
}
I am able to see events fired after the context is loaded or refreshed, is this expected that custom application events can be published after the context loaded or refreshed?.
I am using Spring 4.3.10
Thanks in advance

The #EventListener annotations are processed by the EventListenerMethodProcessor which will run as soon as all beans are instantiated and ready. As you are publishing an event from a #PostConstruct annotated method it might be that not everything is up and running at that moment and #EventListener based methods haven't been detected yet.
Instead what you can do is use the ApplicationListener interface to get the events and process them.
public class MyEventHandler implements ApplicationListener<StateEvent> {
public void onApplicationEvent(StateEvent event) {
System.out.println(event);
}
}

You should publish event after ContextRefreshedEvent occur, but if you wait ContextRefreshedEvent in #PostConstruct, it will make whole applicaiton hang up, so use #Async will solve this problem.
#EnableAsync
#SpringBootApplication
public class YourApplication
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
}
#Slf4j
#Service
public class PublishHelper {
private final ApplicationEventPublisher publisher;
private final CountDownLatch countDownLatch = new CountDownLatch(1);
#EventListener(classes = {ContextRefreshedEvent.class})
public void eventListen(ContextRefreshedEvent contextRefreshedEvent) {
log.info("publish helper is ready to publish");
countDownLatch.countDown();
}
public PublishHelper(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
#Async
#SneakyThrows
public void publishEvent(Object event) {
countDownLatch.await();
publisher.publishEvent(event);
}
}

Related

Async ApplicationListener in Spring compared with async #EventListener

Since Annotation-Driven Event Listener was added in Spring version 4.2, to make an asynchronous event listener it is only needed to add the #Async annotation to the public method annotated with #EventListener.
#Component
public class AnnotationDrivenContextStartedListener {
#Async
#EventListener
public void handleContextStart(ContextStartedEvent cse) {
System.out.println("Handling context started event.");
}
}
The question is the following. Could the #Async annotation be added to the onApplicationEvent method of a traditional ApplicationListener to achieve the same functionality? Or should we define a new bean for the ApplicationEventMulticaster to handle all events in async way?
#Component
public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> {
#Async
#Override
public void onApplicationEvent(CustomSpringEvent event) {
System.out.println("Received spring custom event - " + event.getMessage());
}
}
I have tried it and it works correctly, but I am not sure if it is a good practice or can cause problems.
Thanks in advance.

Spring Boot application run method periodically

I am playing with a simple Spring Boot application and RabbitMQ.
However I cannot figure out how to run a method periodically.
Here is my Application class
#SpringBootApplication
public class SampleApp {
#Autowired
Sender sender;
public static void main(String[] args) {
SpringApplication.run(SampleApp.class, args);
}
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
sender.sendMessage();
}
}
And the sendMessage method is defined as below
#Scheduled(fixedRate = 3000L)
public void sendMessage() {
log.info("Sending message...");
rabbitTemplate.convertAndSend("my-exchange", "my-routing-key", "TEST MESSAGE");
}
However this method is called only once, I can see only a single line in the console.
What I missed in my code?
Thanks.
Looks like you are missing #EnableScheduling:
#EnableScheduling
#SpringBootApplication
public class SampleApp {
...
}
Quoting the documentation:
Enables Spring's scheduled task execution capability, similar to functionality found in Spring's <task:*> XML namespace. To be used on #Configuration classes as follows:
#Configuration
#EnableScheduling
public class AppConfig {
// various #Bean definitions
}
This enables detection of #Scheduled annotations on any Spring-managed bean in the container.
I am usually using the Spring ThreadPoolTaskScheduler. You define it, as Bean for example then you wrap your method into a Runnable and you call it at intervals defined by a CronTrigger. The result can be retrieved using a ScheduledFuture
Check https://www.baeldung.com/spring-task-scheduler for a complete beginner tutorial.
Here's an alternative way if you don't want to rely on Spring's scheduler.
It's using rxjava2, here's an example:
#Component
public class MessagePublisher {
Sender sender;
Disposable d;
#Autowired
public Config(Sender sender) {
this.sender = sender;
this.d = doSomethingAfterStartup();
}
public Disposable doSomethingAfterStartup() {
return Observable.interval(3000, TimeUnit.MILLISECONDS).subscribe(tick -> {
sender.sendMessage();
});
}
}

Best way to initialize beans in Spring context after application started?

I need to initialize beans in the Spring context after my application has started; currently, I initialize beans in a class with annotation #Configuration like this:
#Configuration
public class AppConfig {
#Inject
#Bean
public BeanA init(param1, param2, etc...) {
--- Code to construct bean A ---
}
#Inject
#Bean
public BeanB init(param1, param2, etc...) {
--- Code to construct bean B ---
}
}
But some beans I need to initialize after application startup so my approach is create a class listen to ApplicationReadyEvent event in Spring and put the code to initialize beans in that class.
#Configuration
class ApplicationStartingListener implements ApplicationListener<ApplicationReadyEvent>{
---- Code to init bean here ----
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
--- If I put init bean code in here, is it correct? ----
}
}
Is this the best way? Or there are some other better solutions?
I will enumerate other approaches in order to init beans, I grouped the approach in Standard Approach and Spring Boot Approach.
Standard Approach
#PostConstruct: it is just an annotation that triggers a method after bean is create, it doesn't allow input parameters.
#Bean(init-method="somInitMehotd"): this approach is totally related to Spring bean lifecycle and it is called after bean creation, if you are using another method with #PostConstruct annotation, then the #PostConstruct will be called first. This approach doesn't allow input parameters.
ApplicationListener: this interface allows to listen the standard events related to the Context Lifecycle, also it can listen customized events. For example: create a class MyAppListener and implements ApplicationListener<ContextRefreshedEvent> in this case the MyAppListener will implement an onApplicationEvent method that receives a ContextRefreshedEvent
Spring Boot Approach
The runners: There are two very useful interfaces CommandLineRunner and ApplicationRunner both of them will run after ApplicationContext is created both of them allows to inject beans as input parameters.
Spring boot listeners: Spring Application gives some additional events than the standards events that comes from the Application Context. One of the event is ApplicationReadyEvent and it is fire when the application is ready to receive request. In order to listen this events just implements the ApplicationListener using ApplicationReadyEvent as generic.
Here is the example:
MyBean class has different methods that will be called for each approach listed above, every method will call a print method and that method has a Thread.sleep in order to validate the order that every listener is called.
import javax.annotation.PostConstruct;
public class MyBean {
private String myVar="";
public MyBean(){
}
#PostConstruct
public void postConstructInit(){
this.myVar="Post init called";
print();
}
public void beanInit(){
this.myVar="Bean init called";
print();
}
public void contextInit(){
this.myVar="Context init called";
print();
}
public void runnerInit(){
this.myVar="Runner init called";
print();
}
public void bootListenerInit(){
this.myVar="Boot init called";
print();
}
public void print(){
System.out.println(this.myVar);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Here is the ContextRefreshListener class that will listen the ContextRefreshedEvent and handle it.
public class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit();
}
}
And it is the BootListener that will receive the ApplicationReadyEvent that comes from Spring Application.
public class MyBootListener implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit();
}
}
And finally the Spring Boot Application
#SpringBootApplication
public class StackoverflowBootApplication {
public static void main(String[] args) {
SpringApplication.run(StackoverflowBootApplication.class, args);
}
#Bean(name = "myBean", initMethod = "beanInit")
public MyBean getMyBean(){
return new MyBean();
}
#Bean
public ContextRefreshListener getContextRefreshedListener(){return new ContextRefreshListener();}
#Bean
public MyBootListener getBootListener(){return new MyBootListener();}
#Bean
public CommandLineRunner getRunner(ApplicationContext ctx){
return (args) -> {
ctx.getBean(MyBean.class).runnerInit();
};
}
}
The output is:
Post init called
Bean init called
Context init called
Runner init called
Boot init called
Post init called output comes from
#PostConstruct
public void init(){
this.initByPostconstruct="Post init called";
Bean init called comes from the initMethod value
#Bean(name = "myBean", initMethod = "beanInit")
public MyBean getMyBean(){
return new MyBean();
}
}
Context init called comes from ContextRefreshedEvent
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
contextRefreshedEvent.getApplicationContext().getBean(MyBean.class).contextInit();
}
Runner init called comes from CommandLineRunner
#Bean
public CommandLineRunner getRunner(ApplicationContext ctx){
return (args) -> {
ctx.getBean(MyBean.class).runnerInit();
};
}
Boot init called comes from ApplicationReadyEvent
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
applicationReadyEvent.getApplicationContext().getBean(MyBean.class).bootListenerInit();
}
All the listed scenarios were triggered by Spring, I didi'nt call any of the events directly, all of them were called by Spring Framework.

Configure Spring task scheduler to run at a fixeDelay or run at once based on a boolean

I have a code which runs at a regular interval.
Below is code for that
#EnableScheduling
#Component
public class Cer {
#Autowired
private A a;
#Autowired
private CacheManager cacheManager;
#Scheduled(fixedDelayString = "${xvc}")
public void getData() {
getCat();
getB();
return;
}
}
I want to change#Scheduled(fixedDelayString = "${xvc}") this based on a boolean say runOnce if runOnce is true #scheduled should run once only say on code startup.
Any advice how to achieve this.
Thanks in advance.
I would place the functionality that you want to schedule in a component:
#Component
public class ServiceToSchedule {
public void methodThatWillBeScheduled() {
// whatever
return;
}
}
And have two additional components that will be instantiated depending on a Profile. One of them schedules the task, and the other one just executes it once.
#Profile("!scheduled")
#Component
public class OnceExecutor {
#Autowired
private ServiceToSchedule service;
#PostConstruct
public void executeOnce() {
// will just be execute once
service.methodThatWillBeScheduled();
}
}
#Profile("scheduled")
#Component
#EnableScheduling
public class ScheduledExecutor {
#Autowired
private ServiceToSchedule service;
#Scheduled(fixedRate = whateverYouDecide)
public void schedule() {
// will be scheduled
service.methodThatWillBeScheduled();
}
}
Depending on which profile is active, your method will be executed just once (scheduled profile is not active), or will be scheduled (scheduled profile is active).
You set the spring profiles by (for example) starting your service with:
-Dspring.profiles.active=scheduled

Spring #Async ignored

I am having troubles invoking a method asynchronously in Spring, when the invoker is an embedded library receiving notifications from an external system. The code looks as below:
#Service
public class DefaultNotificationProcessor implements NotificationProcessor {
private NotificationClient client;
#Override
public void process(Notification notification) {
processAsync(notification);
}
#PostConstruct
public void startClient() {
client = new NotificationClient(this, clientPort);
client.start();
}
#PreDestroy
public void stopClient() {
client.stop();
}
#Async
private void processAsync(Notification notification) {
// Heavy processing
}
}
The NotificationClient internally has a thread in which it receives notifications from another system. It accepts a NotificationProcessor in its constructor which is basically the object that will do the actual processing of notifications.
In the above code, I have given the Spring bean as the processor and attempted to process the notification asynchronously by using #Async annotation. However, it appears the notification is processed in the same thread as the one used by NotificationClient. Effectively, #Async is ignored.
What am I missing here?
#Async (as well as #Transactional and other similar annotations) will not work when the method is invoked via this (on when #Async is used for private methods*), as long as you do not use real AspectJ compiletime or runtime weaving.
*the private method thing is: when the method is private, then it must been invoked via this - so this is more the consequence then the cause
So change your code:
#Service
public class DefaultNotificationProcessor implements NotificationProcessor {
#Resource
private DefaultNotificationProcessor selfReference;
#Override
public void process(Notification notification) {
selfReference.processAsync(notification);
}
//the method must not been private
//the method must been invoked via a bean reference
#Async
void processAsync(Notification notification) {
// Heavy processing
}
}
See also the answers for: Does Spring #Transactional attribute work on a private method? -- this is the same problem

Categories