Spring Events and AOP

Agenda

  1. Event Handling in Spring

  2. Introduction to AOP

  3. Writing Aspects

  4. Pointcut and WildCard Expressions

  5. Reusing pointcut definition

  6. After advice

  7. Around advice

  8. 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