INTRODUCTION
The essence of the Observer Pattern is to "Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically." GoF. Observer pattern is a subset of publish/subscribe pattern which allows a number of observer objects to see an event.
This pattern can be used in different situations, but in summary we can say that Observer pattern can be applied when an object should be able to notify messages to other objects, and you don't want these objects being tightly coupled. In my case I have used this pattern when an asynchronous event should be notified to one or more graphical component.
This pattern can be implemented using an adhoc solution or using java.util.Observer/Observable classes. But my projects are always developed with Spring whether they are web or desktop applications. So in current post I will explain how I implement Observer pattern with Spring.
HANDS ON
Event handling in Spring ApplicationContext is provided through ApplicationEvent class and ApplicationListener interface. If a bean that implements ApplicationListener interface is deployed into the context, every time an ApplicationEvent is published to container, ApplicationListener receives it.
Spring comes with built-in events, like ContextStartedEvent, ContextStoppedEvent, but you can also create your own custom events.
For developing your own events, three classes are required, observer role, observable role and the event. Observers are those who receive events and must implement ApplicationListener class. Observable classes are responsible of publishing events and must implement ApplicationEventPublisherAware. Finally event class has to extend ApplicationEvent.
CODING
What I am going to implement is wikipedia example of Observer pattern (http://en.wikipedia.org/wiki/Observer_pattern#Example) but using Spring Events instead of Observer/Observable Java classes. The example is a basic publish/subscribe example where one String message is sent from one module to another one.
Let's create MessageEvent. This event contains a String that represents the message we want to send. It is a simple class that extends from ApplicationEvent.
Next class is the Observable class. This class must implements ApplicationEventPublisherAware. This interface defines a setter method with ApplicationEventPublisher as parameter. This parameter is used for publishing events.
In current implementation see that also implements Runnable interface so user can create from console input, asynchronous messages. Most important line is 26 where an event is created and published.
The Observer class is even simpler. Implements ApplicationListener interface. Method onApplicationEvent is called when an event is published. See that it is a generic interface, so no cast is required. This differs from java.util.Observer class.
In application context file, you register both ApplicationListener and ApplicationEventPublisherAware beans.
And finally a main class to test the system. A thread is created to execute multiple asynchronous events.
So start the program and write something to console. You will see something like:
hello
Thread-0
Thread-0
MessageEvent [message=hello]
I have entered "hello" message and thread name of event publisher is printed. Then event is sent and handler thread name is printed too. Finally the received event is shown. There is one thing that should call your attention. Both sender (Observable) and receiver (Observer) are executed in same thread; by default event listeners receive events synchronously. This means that publishEvent() method, blocks until all listeners have finished processing the event. This approach has many advantages (for example reusing transaction contexts, ...), but in some cases you will prefer that each event is executed in new thread, Spring also supports this strategy.
In Spring, class responsible of managing events is SimpleApplicationEventMulticaster. This class multicasts all events to all registered listeners, leaving it up to the listeners to ignore events that they are not interested in. Default behaviour is that all listeners are invoked in calling thread.
Now I am going to explain how Spring Event Architecture is initialized and how you can modify. By default when ApplicationContext is started up, it calls initApplicationEventMulticaster method. This method verify if exists a bean with id applicationEventMulticaster of type ApplicationEventMulticaster. If it is the case defined ApplicationEventMulticaster is used, if not a new SimpleApplicationEventMulticaster with default configuration is created.
SimpleApplicationEventMulticaster has a setTaskExecutor which can be used for specifying which java.util.concurrent.Executor will execute events. So if you want that each event is executed in a different thread, a good approach would be using a ThreadPoolExecutor. As explained in last paragraph, now we must explicitly define SimpleApplicationEventMulticaster instead of using default ones. Let's implement:
First of all SimpleApplicationEventMulticaster must be defined as a bean with id applicationEventMulticaster. Then task pool is set, and we rerun our main class. And output will be:
hello
Thread-1
pool-1
MessageEvent [message=hello]
Note that now sender and receiver thread is different.
And of course you can create your own ApplicationEventMulticaster for more complex operations. You just have to implement ApplicationEventMulticaster and defining it with applicationEventMulticaster bean name, and events will be executed depending on your own strategy.
Hope that now your Spring desktop applications can take full advantage of Spring events for separating modules.
How would this work in a JEE environment? Most appliction servers don't want you to spawn a new thread since you will be using the JEE context. App servers like WAS have their own thread pool and a factory for a runnable. So, does that mean that your solution is not appropriate for a JEE app?
ResponderEliminarHi, you can also use this approach in JEE environment, Spring container is responsible of managing pool of threads, of course you should take in consideration how many connections will you receive, and so you can tune pool of threads correctly.
ResponderEliminarThank you so much for reading my blog.
Even while it can appear like the first step is the easiest, it's actually the most difficult ukwritings review
ResponderEliminar