Spring's ApplicationContext provides the functionality to support events and listeners in code. Spring allows us to create listeners in the code. We can create beans that listen for events which are published through our ApplicationContext. This is achieved via the ApplicationEventPublisher interface.
Now to create the listeners.
The first step was to create some beans that can listen to events:
The above class listens for all events generated by the ApplicationContext. The presence of generics in the Listener interface allows us to listen for very specific events. In this case our listener listens to all events of type ApplicationEvent.
Let us look at one more Listener:
Spring is not restricted to using these predefined events. It allows us to define and publish Custom events too. I created one such custom event.
As per the code comments this is
The spring configuration file is as below:
public interface ApplicationEventPublisher { /** * Notify all listeners registered with this application of an application * event. Events may be framework events (such as RequestHandledEvent) * or application-specific events. * @param event the event to publish * @see org.springframework.web.context.support.RequestHandledEvent */ void publishEvent(ApplicationEvent event); }The interface exposes a single method - one that allows us to publish events. The ApplicationContext implements the above interface.
Now to create the listeners.
The first step was to create some beans that can listen to events:
public class AllApplicationEventListener implements ApplicationListener<ApplicationEvent> { @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { System.out.println(" AllApplicationEventListener (with hashcode) " + this.hashCode() + "\n received " + applicationEvent.getClass() + "\n at " + formatDate(applicationEvent.getTimestamp()) + "\n with Source as " + applicationEvent.getSource().getClass()); } }For listening to events that are published a class needs o implement the ApplicationListener interface.
The above class listens for all events generated by the ApplicationContext. The presence of generics in the Listener interface allows us to listen for very specific events. In this case our listener listens to all events of type ApplicationEvent.
Let us look at one more Listener:
public class ContextStartedEventListener implements ApplicationListener<ContextStartedEvent> { @Override public void onApplicationEvent(ContextStartedEvent event) { System.out.println(" ContextStartedEventListener received " + event.getClass() + "\n at " + formatDate(event.getTimestamp()) + "\n with Source as " + event.getSource().getClass() + "\n for application context " + event.getApplicationContext().getClass()); } }The ContextStartedEvent is one of the events generated by the ApplicationContext. It extends the generic (and abstract) ApplicationEvent.
Spring is not restricted to using these predefined events. It allows us to define and publish Custom events too. I created one such custom event.
public class CustomMsgEvent extends ApplicationEvent { final String msg; public String getMsg() { return msg; } public CustomMsgEvent(Object source, final String msg) { super(source); this.msg = msg; System.out.println("Created a Custom event"); } @Override public String toString() { return "CustomMsgEvent msg: " + this.msg; } }The base class defines a constructor which takes an object as a parameter.
As per the code comments this is
the component that published the event
I created a listener to listen for the custom event.
public class CustomEventListener implements ApplicationListener<CustomMsgEvent> { @Override public void onApplicationEvent(CustomMsgEvent applicationEvent) { System.out.println(" CustomEventListener received " + applicationEvent.getClass() + "\n at " + formatDate(applicationEvent.getTimestamp()) + "\n with Source as " + applicationEvent.getSource().getClass()); System.out.println("The message is " + applicationEvent.getMsg()); } }Now to run the code.
The spring configuration file is as below:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="allAppEventListener" class="com.listener.AllApplicationEventListener"/> <bean id="contextStartedEventListener" class="com.listener.ContextStartedEventListener"/> <bean id="customEventListener" class="com.listener.CustomEventListener"/> </beans>We have defined our listeners as beans in the configuration file. The test code is as below:
public class Test { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-beans.xml"); System.out.println("Application context has been started - publishing message"); CustomMsgEvent customMsgEvent = new CustomMsgEvent(applicationContext, "Test message"); applicationContext.publishEvent(customMsgEvent); //-blocking call i.e. synchronous System.out.println("The custom event was published successfully"); System.out.println("Starting the applicationContext"); ((ConfigurableApplicationContext)applicationContext).start(); System.out.println("Stopping the applicationContext"); ((ConfigurableApplicationContext)applicationContext).stop(); } }On running the code the logs are:
875 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationCon text - Unable to locate ApplicationEventMulticaster with name 'applicationEvent Multicaster': using default [org.springframework.context.event.SimpleApplication EventMulticaster@15a0305] ... 1000 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Publishing event in org.springframework.context.support.ClassPathXmlApplicationContext@b179c3: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework. context.support.ClassPathXmlApplicationContext@b179c3: startup date [ Sun Dec 02 15:04:19 IST 2012]; root of context hierarchy] AllApplicationEventListener (with hashcode) 13238549 received class org.springframework.context.event.ContextRefreshedEvent at 3:04:20 PM with Source as class org.springframework.context.support.ClassPathXmlApplicationContext Application context has been started - publishing message ... Created a Custom event 1016 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Publishing event in org.springframework.context.support.ClassPathXmlApplicationContext@b179c3: CustomMsgEvent msg: Test message AllApplicationEventListener (with hashcode) 13238549 received class com.listener.CustomMsgEvent at 3:04:20 PM with Source as class org.springframework.context.support.ClassPathXmlApplicationContext CustomEventListener received class com.listener.CustomMsgEvent at 3:04:20 PM with Source as class org.springframework.context.support.ClassPathXmlApplicationContext The message is Test message The custom event was published successfully ... Starting the applicationContext 1031 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Publishing event in org.springframework.context.support.ClassPathXmlApplicationContext@b179c3: org.springframework.context.event.ContextStartedEvent[source=org.springframework. context.support.ClassPathXmlApplicationContext@b179c3: startup date [ Sun Dec 02 15:04:19 IST 2012]; root of context hierarchy] AllApplicationEventListener (with hashcode) 13238549 received class org.springframework.context.event.ContextStartedEvent at 3:04:20 PM with Source as class org.springframework.context.support.ClassPathXmlApplicationContext ContextStartedEventListener received class org.springframework.context.event.ContextStartedEvent at 3:04:20 PM with Source as class org.springframework.context.support.ClassPathXmlApplicationContext for application context class org.springframework.context.support.ClassPathXmlApplicationContext ... Stopping the applicationContext 1031 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Publishing event in org.springframework.context.support.ClassPathXmlApplicationContext@b179c3: org.springframework.context.event.ContextStoppedEvent[source=org.springframework. context.support.ClassPathXmlApplicationContext@b179c3: startup date [ Sun Dec 02 15:04:19 IST 2012]; root of context hierarchy] AllApplicationEventListener (with hashcode) 13238549 received class org.springframework.context.event.ContextStoppedEvent at 3:04:20 PM with Source as class org.springframework.context.support.ClassPathXmlApplicationContextThe logs in red are console statements while those in blue are Spring logs. The conclusions to be drawn from above are:
- When the Application context published events, they were received by the AllApplicationEventListener.
- On publishing the ContextStartedEvent, it was received by both the listeners.
- As can be seen from the logs the event publishing mechanism is synchronous. Only after the onApplicationEvent method of all the listeners had executed did the publishEvent method of the ApplicationContext complete.
- If there was any transactional context, then all our listener methods would have executed within the same.
- It may often be necessary that we need an asynchronous kind of flow. The Container publishes the event and simply moves on. It does not wait for the listener methods to complete execution. This can be achieved by the SimpleApplicationEventMulticaster class.
No comments:
Post a Comment