Spring Framework Intermediate

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

  9. Understanding SpEL

  10. Writing spring expression

  11. Passing Objects to expressions

  12. SpEL with Spring Bean                                      (i) using systemProperties

     (ii) Assigning values from property file

     (iii) Collections with SpEL

     (iv) Method factory wiring

     (v) Static factory wiring                                                                                      

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.

Exercise 1

  • Create a class Database with 2 instance variables port and name.
  • Create a method in the Database class connect()
  • Configure the bean of type database in spring configuration file.
  • Get the bean of the type Database from Spring config.
  • Print the values of the instance variables.
  • Invoke context events start(), stop() and close().
  • Create listeners for spring events.
  • Create a CustomEvent which should get published when you invoke connect method of database bean.

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");
}

Exercise 2

  • Try all AOP wild expressions which we discussed, on the Database class which we created in the previous exercise.
  • Apply Before, After, AfterReturning, AfterThrowing and Around Advice for the methods of the database class.

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);
    }
}

Exercise 3

  • Apply execution, within, args, this and bean expressions on the before and after advice of database bean.
  • Reuse the expressions for getters and setters with the help of @Pointcut annotation and use || operator to run before advice on both the getters but after advice only one getter.
  • Access the properties of the JoinPoint in before advice.

Use cases of AOP

  • Logging

 

  • Auditing

 

  • Security

What is SpEL ?

  • The Spring Expression Language (SpEL) is a powerful expression language that support quering and manipulating an object at runtime.
  • Spel expressions can be used with XML or annotation based configurations for defining Bean Definations. In both the cases the syntax to define the expression is of the form  #{<expressions>}

     

 

Where we can use SpEL ?

  • Inject a bean.
  • Placing a bean property in another bean.
  • Invoking a bean method in another bean.
  • It supports mathmatical, relational, logical operations at runtime.
  • Get the values of Map, List and Objects at runtime.

Writing a spring expression

  • Writing a spring expression
  • Create instance of Expression by calling parseExpression method on SpelExpressionParser instance.
  • Get the desired result by calling getValue() on expression object.
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class SpelDemo {
    public static void main(String[] args) {
        ExpressionParser expressionParser = new SpelExpressionParser();
        Expression expression = expressionParser.parseExpression("'hello'");
        System.out.println(expression.getValue());
    }
}

What all can we do inside Spring expression?

  • Literal expressions
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("1");
System.out.println(expression.getValue());
  • Inline List
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("{1,2,{3,4},5,6}");
System.out.println(expression.getValue());
  • Inline Map
 ExpressionParser expressionParser = new SpelExpressionParser();
 Expression expression = expressionParser.parseExpression("{framework:'Spring',version:4}");
 System.out.println(expression.getValue());
  • Array construction
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("new int[]{1,2,3,4}");
System.out.println(expression.getValue());
  • Methods invocation
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("'hello'.toUpperCase()");
System.out.println(expression.getValue());
  • Relational Operations
 ExpressionParser expressionParser = new SpelExpressionParser();
 Expression expression = expressionParser.parseExpression("2==2");
 System.out.println(expression.getValue());
  • Logical Operations and Mathmatical Operations
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("true and false");
System.out.println(expression.getValue());
expression = expressionParser.parseExpression("1+1");
System.out.println(expression.getValue());

Passing Object to the expression

  • Data can be passed to a spring expression via an instance of StandardEvaluationContext.
  • If you already have the Object then pass it to the constructor of StandardEvaluationContext.
import com.spring.demo.Employee;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class SpelDemo {
    public static void main(String[] args) {
        Employee employee = new Employee("Dummy Employee", 26);
        StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext(employee);
        ExpressionParser expressionParser = new SpelExpressionParser();
        Expression expression = expressionParser.parseExpression("name");
        System.out.println(expression.getValue(standardEvaluationContext));
    }
}
  • We can also create data with the help of setVariable method provided by the instance of StandardEvaluationContext.
  • Pass the context object to the getValue() of the expression object to get the desired result.
import com.spring.demo.Employee;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class SpelDemo {
    public static void main(String[] args) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        ExpressionParser expressionParser=new SpelExpressionParser();
        Expression expression = expressionParser.parseExpression("(#a * #b)+#c");
        context.setVariable("a", 2);
        context.setVariable("b", 4);
        context.setVariable("c", 1);
        System.out.println(expression.getValue(context));
    }
}

Exercise 4

  • Create a class Person with instance variables name, age  and city.
  • Fetch the value of one of the instance variable using SpEL.
  • Perform Mathmatical calculation as follows using StandardEvaluationContext.

Spel with Spring Beans

  • #{<spring-expression>} can be used with @Value annotation in order to assign value to the properties of bean.
import org.springframework.beans.factory.annotation.Value;
import java.util.List;
import java.util.Map;

public class SpelBean {

    @Value("#{'SpEL'}")
    String name;

    @Value("#{23}")
    Integer age;

    @Value("#{{1,2,3,4}}")
    List list;

    @Value("#{{a:1,b:2,c:3}}")
    Map map;

// getters of all instance variables
}
  • Note: Setters are not required in this case.
  • SpEL can also be used to set values in spring beans in Spring Configuration File.  
<bean id="spelBean2" class="com.spring.demo.SpelBean">
        <property name="age" value="#{23}"/>
        <property name="name" value="#{'config bean'}"/>
        <property name="list" value="#{{9,8,7,6}}"/>
        <property name="map" value="#{{a:1,b:2}}"/>
</bean>
  • Value of the bean can also be set by systemProperties.
@Value("#{systemProperties['user.country']}")
String country;

Note: In order to use systemProperties you need to place this line in spring configuration file.

<context:annotation-config/>

 Getting value from a file and placing it in the instance variable of a bean

  • Create an application.properties file and place following content in it inside resources folder.
valueFromFile=Delhi
  • Now we need to tell Spring path of our file we will do it by placing following line in spring configuration file.
<context:property-placeholder location="classpath:application.properties"/>
  • Now we can set the value of instance variable of a bean by properties file with the help of $ sign in following way:
    @Value("${valueFromFile}")
    String state;

Exercise 5

  • Create a class Dummy with instance variables of Type Integer, String, Map and List.
  • Initialize instance variables with @Value annotation using SpeL.
  • Introduced two more variable countryCode and state in Dummy class. Set countryCode via systemProperties and state via property file.

Using Operators with Spring Beans

  • Create a class PersonalisedService as follows:
public class PersonalisedService {
    Integer age;
    Integer incrementedAge;
    Boolean isAgeGreaterThan10;
    Integer firstElementOfList;
    Integer mapValue;
    // generate getters and setters
}
  • Now set the values of its instance variables with the values of existing spelBean as follows:
<bean id="personalisedService" class="com.spring.demo.PersonalisedService">
        <property name="age" value="#{spelBean.age}"/>
        <property name="incrementedAge" value="#{spelBean.age + 1}"/>
        <property name="isAgeGreaterThan10" value="#{spelBean.age>10}"/>
        <property name="firstElementOfList" value="#{spelBean.list[0]}"/>
        <property name="mapValue" value="#{spelBean.map[a]}"/>
</bean>

Static Method wiring

  • For the reference of a static method we need to give fully qualified name with following syntax “#{T(full-qualified-name).staticMethod()}“

 

@Value("#{T(com.spring.demo.EmployeeService).getEmployeeViaStaticMethod()}")
Employee employee;

Method Factory Wiring

  • You can call an instance method of a spring bean inside spring expression language.

 

@Value("#{employeeService.getEmployeeViaInstanceMethod()}")
Employee employee;

Exercise 6

  • Create a PersonalizedService class as shown in slides and apply different operators for assigned the value to its instance variables.
  • Create a PersonService with one static method and one instance method both should return one instance of Person class.
  • Introduce a new field of type person in  PersonalizedService and initialize it via static method and then via instance method.

Thank you :)

Spring Framework Intermediate

By Pulkit Pushkarna

Spring Framework Intermediate

  • 2,137