Spring Events and AOP
Agenda
-
Event Handling in Spring
-
Introduction to AOP
-
Writing Aspects
-
Pointcut and WildCard Expressions
-
Reusing pointcut definition
-
After advice
-
Around advice
-
Custom advice annotation
Spring Events
- An event is an action or occurrence recognised and handled by the software.
- Event handling in ApplicationContext is provided through the ApplicationEvent class and ApplicationListener interface.
- If a bean that implements the ApplicationListener interface is deployed into the context, every time an ApplicationEvent gets published to the ApplicationContext, that bean is notified.
Built In Spring events
- ContextRefreshedEvent : This event is published when the ApplicationContext is either initialized or refreshed.This can also be raised using the refresh() method on theConfigurableApplicationContext interface.
- ContextStartedEvent :This event is published when the ApplicationContext is started using the start() method on the ConfigurableApplicationContext interface.
- ContextStoppedEvent : This event is published when the ApplicationContext is stopped using the stop() method on the ConfigurableApplicationContext interface.
- ContextClosedEvent : This event is published when the ApplicationContext is closed using the close() method on the ConfigurableApplicationContext interface. A closed context reaches its end of life; it cannot be refreshed or restarted.
Publishing Spring Events
- Create a class Dummy
public class Dummy {
public void display() {
System.out.println("Display");
}
}
- Configure the dummy bean in spring configuration file
<bean id="dummy" class="com.spring.demo.Dummy"/>
- Initialize Application context and invoke start and stop method on the context
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("config.xml");
ctx.start();
Dummy dummy = ctx.getBean("dummy", Dummy.class);
dummy.display();
ctx.stop();
Creating an Event Listener for Spring events
- Create a class MyEventListener which implements ApplicationListener interface.
- Implement onApplicationEvent method of ApplicationListener interface inside MyEventListener.
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
public class MyEventListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println(event);
}
}
- Register MyApplicationListener inside spring configuration file.
<bean class="com.spring.event.MyEventListener"/>
Capture a specific spring event
- In order to catch a specific spring event we need to mention the name of that event class in generics with ApplicationListener
package com.spring.event;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;
public class MyEventListener implements ApplicationListener<ContextStartedEvent> {
@Override
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println(event);
}
}
Creating a custom Spring Event
- Create a class CustomEvent which extends ApplicationEvent.
- Define a constructor inside CustomEvent class which accepts one argument of type Object and pass that object to super class.
import org.springframework.context.ApplicationEvent;
public class CustomEvent extends ApplicationEvent {
CustomEvent(Object object) {
super(object);
}
}
import com.spring.CustomEvent;
import org.springframework.context.ApplicationListener;
public class CustomEventListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println(event.getSource());
}
}
Create custom Event Listener
- Create a class CustomEventListener which implements ApplicationListener interface and specify CustomEvent in generic with it.
- Configure customEventListener bean in spring configuration file.
<bean class="com.spring.listener.CustomEventListener"/>
Introduce ApplicationEventPublisher
- Implement ApplicationEventPublisherAware in Dummy class.
- Define an instance variable of type ApplicationEventPublisher and set the its value via setApplicationEventPublisher which is provided by ApplicationEventPublisherAware interface.
- Now in order publish the event create an instance of CustomEvent with passing the instance of Dummy class as constructor arument.
- Now invoke publishEvent method with applicationEventPublisher and pass the CustomEvent instance in method as argument.
import com.spring.CustomEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
public class Dummy implements ApplicationEventPublisherAware {
ApplicationEventPublisher applicationEventPublisher;
void display() {
CustomEvent customEvent = new CustomEvent(this);
applicationEventPublisher.publishEvent(customEvent);
System.out.println("Display");
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
} }
Using event Listener annotation
- By using @EventListener annotation we can define we can define multiple event listeners in single class in following way:
import org.springframework.context.event.*;
public class MultipleEventListener {
@EventListener(ContextStartedEvent.class)
void start() {
System.out.println("----------------start");
}
@EventListener(ContextStoppedEvent.class)
void stopped() {
System.out.println("----------------stopped");
}
}
- We need to place following line in spring configuration file in order to use this annotation.
<context:annotation-config/>
Running spring event in separate Thread
- In order to run event in separate thread we need to make following configurations in spring configuration file
<bean id="simpleAsyncTaskExecutor"
class="org.springframework.core.task.SimpleAsyncTaskExecutor"/>
<bean id="applicationEventMulticaster"
class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="simpleAsyncTaskExecutor"/>
</bean>
Advantages of Spring Events
- Components are loosely coupled.
- You might need to take more than one independent actions when event occurs.
- The processing needs to be handed off to another thread to prevent blocking, e.g. sending an email.
AOP (Aspect Oriented Programming)
- Aspect Oriented Programming complements Object-Oriented Programming by providing another way of thinking about program Structure.
- The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the Aspect.
- Aspect enables the modularization of concerns that cut across multiple types and objects.
AOP terminologies
- Aspect : A modularization of a concern that cuts across multiple classes.
- Joint Point : A point during the execution of a program.
- Advice : Action taken by an aspect at a particular joint point.
- Pointcut : a predicate that matches joint points.
AOP in action
- Enable aspectj autoproxy
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<aop:aspectj-autoproxy/>
</bean>
AOP in action (Cont.)
- Create a class Dummy
class Dummy{
public void display(){
System.out.println(“Dummy class display method“);
}}
- Configure Dummy bean in spring configuration file.
<bean id="dummy" class="com.spring.demo.Dummy">
AOP in action (Cont.)
- Create an aspect class and mark it with annotation @Aspect.
- create a method inside MyAspect class with the name beforeAdvice and mark it with before annotation in following way.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
@Before("execution(public void display())")
void beforeAdvice() {
System.out.println("Before advice is running");
}
}
- Configure MyAspect bean in Spring Configuration file.
Wild Expressions for AOP
- @Before(execution(void display())) will envoke display an advice before any method that has the signature void display()
@Before("execution( void display())")
void beforeAdvice() {
System.out.println("Running before advice");
}
- @Before(execution(* get*())) will envoke an advice before any getter method
@Before("execution( * get*())")
void beforeAdvice() {
System.out.println("Running before advice");
}
- @Before("execution(* com.spring.demo.Dummy.*())") will envoke an advice before all the methods inside Dummy class which does not take any argument.
@Before("execution( * com.spring.demo.Dummy.*())")
void beforeAdvice() {
System.out.println("Running before advice");
}
Wild Expressions for AOP (Cont.)
- @Before("execution(* com.spring.demo.*.*())") will envoke an advice before all the methods inside com.spring.demo package which accepts no parameters.
@Before("execution( * com.spring.demo.Dummy.*())")
void beforeAdvice() {
System.out.println("Running before advice");
}
- @Before("execution(void *())") will envoke an advice before any method with void return type but does not accept any parameter.
@Before("execution(void *())")
void beforeAdvice() {
System.out.println("Running before advice");
}
- @Before("execution(* *())") will envoke an advice before any method which takes no argument
@Before("execution(* *())")
void beforeAdvice() {
System.out.println("Running before advice");
}
Wild Expressions for AOP (Cont.)
- @Before("execution(* *(..))") Will envoke an advice before any method
@Before("execution(* *(..))")
void beforeAdvice() {
System.out.println("Running before advice");
}
Types of Advice
- Before : Run advice before the a method execution.
@Before("execution(void display())")
void beforeAdvice() {
System.out.println("Running before advice");
}
- After-returning : Run advice after the a method execution only if method completes successfully.
@AfterReturning(pointcut = "execution(Integer getInteger())", returning = "returnValue")
void afterReturningAdvice(Integer returnValue) {
System.out.println("Running AfterReturning " + returnValue);
}
- After-throwing : Run advice after the a method execution only if method exits by throwing an exception.
@AfterThrowing(pointcut = "execution(void throwException())", throwing = "ex")
void afterReturningAdvice(Exception ex) {
System.out.println("Running AfterThrowing " + ex);
}
Types of Advice (Cont.)
-
After (finally): Run advice after the a method execution regardless of its outcome.
@After("execution(void display())")
void afterAdvice(){
System.out.println("Running after advice");
}
- Around : Run advice before and after the advised method is invoked.
@Around("execution(void display())")
void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Around before");
proceedingJoinPoint.proceed();
System.out.println("Around after");
}
Pointcut Expressions
- execution for matching method execution join points.
- within limits matching to join points within certain types.
- bean limit the matching of join points to a particular named Spring bean.
@Before("execution(void display())")
void beforeAdvice() {
System.out.println("Running before advice");
}
@Before("within(com.spring.demo.*)")
void beforeAdvice() {
System.out.println("Running before advice");
}
@Before("bean(dummy))")
void beforeAdvice() {
System.out.println("Running before advice");
}
Pointcut Expressions (Cont.)
- args limits matching to join points where the arguments are instances of the given types.
@Before("args(Integer)")
void beforeAdvice() {
System.out.println("Running before advice");
}
@Before("this(com.spring.demo.Dummy)")
void beforeAdvice() {
System.out.println("Running before advice");
}
- this limits matching to join points within certain types.
Reusing pointcut
- We can define a pointcut via @Pointcut annotation and then use the name of the method where on which point cut expression is defined in other expressions.
- We can also use || operator to provide the cobination od different pointcuts on which advice will run.
@Before("dummyClassPointcut() || getStringPointcut()")
void beforeAdvice() {
System.out.println("Running before advice");
}
@After("displayPointcut()")
void afterAdvice(){
System.out.println("Running after advice");
}
@Pointcut("execution(void display())")
void displayPointcut(){}
@Pointcut("execution(String getString())")
void getStringPointcut(){}
Accessing JoinPoint in advice
- A joinpoint can be accessed within an advice with the help of an instance of JoinPoint class which should be passed as first parameter to an advice.
@Before("execution(Integer getInteger(Integer))")
void beforeAdvice(JoinPoint joinPoint) {
System.out.println("Running before advice");
System.out.println(joinPoint);
System.out.println(joinPoint.getThis());
System.out.println(joinPoint.getSignature());
Object [] objects=joinPoint.getArgs();
for (Object object:objects){
System.out.println(object);
}
}
Use cases of AOP
- Logging
- Auditing
- Security
Thank you :)
Events and AOP
By Pulkit Pushkarna
Events and AOP
- 914