Spring - Event Handling and AOP
In this tutorial, we will learn to handle events and design our own custom events with an introduction to aspect-oriented programming in Spring framework.
As already stated in our previous articles in this series, Spring framework is an open source Java platform that provides MVC infrastructure support for developing robust Java applications very easily and very rapidly using recommended MVC pattern.
Events
Events are one of the most overlooked functionalities in the framework but also one of the most useful. Like many other things in Spring, event publishing is one of the capabilities provided by ApplicationContext.
There are a few simple steps to follow:
- the event should extend ApplicationEvent
- the publisher should inject an ApplicationEventPublisher object
- the listener should implement the ApplicationListener interface
Custom Events
Spring enables us to create custom events which are by default synchronous. This offers an important advantage which is the listener being able to participate in the publisher’s transaction context.
Let’s create a simple event class—just a placeholder to store the event data. In this case, the event class holds a String message:
public class CustomEvent extends ApplicationEvent {
private String message;
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
Now let’s create a publisher of that event. The publisher constructs the event object and publishes it to anyone who’s listening. To publish the event, the publisher can simply inject the ApplicationEventPublisher and use the publishEvent() API:
public class CustomSpringEventPublisher {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void doStuffAndPublishAnEvent(final String message) {
System.out.println("Publishing custom event. ");
CustomEvent customSpringEvent = new CustomEvent(this, message);
applicationEventPublisher.publishEvent(customSpringEvent);
}
}
Finally, let’s create the listener.
The only requirements for the listener are to be a bean and implement ApplicationListener interface:
@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println("Received spring custom event - " + event.getMessage());
}
}
It’s worth noticing how the custom listener is parametrized with the generic type of custom event, which makes the onApplicationEvent() method type safe. This also avoids having to check if the object is an instance of a specific event class and casting it.
Further, as already discussed, spring events are synchronous by default. The doStuffAndPublishAnEvent() method blocks until all listeners finish processing the event.
Asynchronous Events
In some cases, publishing events synchronously isn’t really what we’re looking for—we may need asynchronous handling of our events.
We can turn that on in the configuration by creating an ApplicationEventMulticaster bean with an executor; for our purposes here SimpleAsyncTaskExecutor works well:
@Configuration
public class AsynchronousSpringEventsConfig {
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster
= new SimpleApplicationEventMulticaster();
eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
return eventMulticaster;
}
}
The event, the publisher, and the listener implementations remain the same as before—but now, the listener will asynchronously deal with the event in a separate thread.
Aspect Oriented Programming
AOP is a way to change existing classes in a project to embellish them or change their behavior based on rules defined separately. This change can be done either before the classes are put into a jar/war, or can happen dynamically while the code is being loaded.
The idea is, rather than finding all the points of code that we want to change in the source code and hand modifying them, we define rules for how to find points of interest in the code base, and what embellishments we would like to do to them. These rules are called aspects (the A of AOP).
The prototypical example is when you want to get some timing information on various methods in your code base.
The most common use is probably the declarative transaction handling using @Transactional.
Some other common examples of these could be:
- Logging
- Exception Handling (especially when we may want to have detailed traces or have some plan of recovering from exceptions)
- Security aspects
- Instrumentation
Advice
Advice is an action taken by an aspect at a particular join point. Different types of advice include “around,” “before” and “after” advice. The main purpose of aspects is to support cross-cutting concerns, such as logging, profiling, caching, and transaction management.
Enable Advice
With Spring, you can declare advice using AspectJ annotations, but you must first apply the @EnableAspectJAutoProxy annotation to your configuration class, which will enable support for handling components marked with AspectJ’s @Aspect annotation.
@Configuration
@ComponentScan(basePackages = {"com.discoversdk.dao", "com.discoversdk.aop"})
@EnableAspectJAutoProxy
public class TestConfig {
...
}
Before Advice
This advice, as the name indicates, is executed before the join point. It doesn’t prevent the continued execution of the method it advises unless an exception is thrown.
@Component
@Aspect
public class LoggingAspect {
private Logger logger = Logger.getLogger(LoggingAspect.class.getName());
@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryMethods() {};
@Before("repositoryMethods()")
public void logMethodCall(JoinPoint jp) {
String methodName = jp.getSignature().getName();
logger.info("Before " + methodName);
}
}
The logMethodCall advice will be executed before any repository method defined by the repositoryMethods pointcut.
After Advice
Suppose that we want to notify some application components when a new instance of Foo is created. We could publish an event from FooDao, but this would violate the single responsibility principle. Instead, we can accomplish this by defining the following aspect:
@Component
@Aspect
public class PublishingAspect {
private ApplicationEventPublisher eventPublisher;
@Autowired
public void setEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryMethods() {}
@Pointcut("execution(* *..create*(Long,..))")
public void firstLongParamMethods() {}
@Pointcut("repositoryMethods() && firstLongParamMethods()")
public void entityCreationMethods() {}
@AfterReturning(value = "entityCreationMethods()", returning = "entity")
public void logMethodCall(JoinPoint jp, Object entity) throws Throwable {
eventPublisher.publishEvent(new FooCreationEvent(entity));
}
}
Notice first that by using the @AfterReturning annotation we can access the target method’s return value. Second, by declaring a parameter of the type JoinPoint, we can access the arguments of the target method’s invocation.
Next we create a listener which will simply log the event.
@Component
public class FooCreationEventListener implements ApplicationListener<FooCreationEvent> {
private Logger logger = Logger.getLogger(getClass().getName());
@Override
public void onApplicationEvent(FooCreationEvent event) {
logger.info("Created foo instance: " + event.getSource().toString());
}
}
AoP vs IoC
Have you searched the web for IoC and AOP? There are a lot of references to both.
In a nutshell, IoC allows an external force to determine what implementation will be used by the code rather than the code determining the implementation. The "external force" might be a configuration file, a unit test, other different code, etc.
AOP allows cross-cutting concerns to be implemented outside of the code affected by those concerns.
The "purpose" of Spring includes IoC and AOP, but goes quite a ways beyond that in its scope.
Conclusion
In this quick tutorial we went over the basics of dealing with events in Spring: creating a simple custom event, pushing it, and then handling it in a listener. We also had a brief look at how to enable asynchronous processing of events in the configuration. Finally we had a look at Aspect-oriented programming in Spring framework.
Check back soon for more articles on Spring framework.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment