Spring Framework Intermediate
Agenda
-
Event Handling in Spring
-
Introduction to AOP
-
Writing Aspects
-
Pointcut and WildCard Expressions
-
Reusing pointcut definition
-
After advice
-
Around advice
-
Custom advice annotation
-
Understanding SpEL
-
Writing spring expression
-
Passing Objects to expressions
-
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,115