Spring AOP

What is AOP(Aspect Oriented Programming)?

AOP is a programming methodology which helps to manage cross cutting concern like transaction management, logging,security etc

Why AOP?

It provides the pluggable way to dynamically add the additional concern before,after or around the actual business logic.

Aspects enable modularization of concerns.

Uses of Spring AOP

Remove tight coupling between cross cutting concerns and business logic
Easy to maintain code in present and future.

AOP Core Concepts

Please refer below diagram

Aspect
An Aspect is a class that implements the Java Enterprise application concerns which cuts through multiple classes like transaction management,logging,security etc.

An aspect will be configured using annotation @Aspect.

To enable AspectJ annotations in Spring projects, we need to configure @EnableAspectJAutoProxy in the aspect class and incase of spring boot project we need to configure @EnableAspectJAutoProxy in main class.

Advice
Advice represents an action taken by an aspect at a particular join point.

1.@Before — Adviced to execute before the joint point.
2.@AfterReturning — Adviced to execute after the joint point complete.
3.@After — Adviced to execute after the joint point.
4.@AfterThrowing- Adviced to execute after the joint point exists by throwing an exception
5.@Around — Advice that surrounds a joint point like method invocation

Joinpoint

A point in a program such as method execution,exception handling etc.

Pointcut

A pointcut is an expression language of AOP that matches joint points which determines whether Advice needs to be executed or not.

Advisor
An advisor is a combination of a pointcut and an advice.
An advisor knows about what advices are needed for what joinpoints identified by a pointcut.

Target & Proxy

Target is a business class object, before AOP feature is applied for it.

Proxy is also a business class object, after AOP feature is applied to it.

Weaving

Linking aspects with other application types or objects to create an advised object. This can be done at compile time (using the AspectJ compiler, for example), load time, or at runtime. Spring AOP, like other pure Java AOP frameworks, performs weaving at runtime.

Weaving is a process of converting a target object into a proxy object by adding advices to it.
For example, a web-container converts a jsp into equivalent servlet. Here jsp is a target and servlet is a proxy. This process is called weaving.

Annotations Allowed Attributes

@Pointcut value
@Before value
@AfterReturning pointcut, value, returning
@After value
@AfterThrowing pointcut, value, throwing
@Around value

Dependencies

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>

</dependency>

Here with the example for @Before,@After,@AfterReturning,@AfterThrowing and @Around.

package com.praveen.aop.service;

import org.springframework.stereotype.Component;

@Component
public class EmployeeService
{
public String addEmployee()
{
System.out.println(“Add Employee “);
String name = null;
name.toLowerCase();
return “Employee Peter information is added successfully”;
}

public void modifyEmployee()
{
System.out.println(“Modify Employee”);
}

public void deleteEmployee()
{
System.out.println(“Delete Employee”);
}

}

package com.praveen.aop.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.praveen.aop.service.EmployeeService;

@Configuration
@ComponentScan(basePackages = “com.praveen.aop”)
public class EmployeeConfig {
@Bean(name=”employeeService”)
public EmployeeService getBean() {
return new EmployeeService();
}

}

package com.praveen.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

@Aspect
@Component
@EnableAspectJAutoProxy
public class LoggingAspect
{

@Before(“execution(* com.praveen.aop.service.EmployeeService.addEmployee())”)
public void logBefore(JoinPoint joinPoint)
{

System.out.print(“logBefore() is running!”);
System.out.println(“, before “
+ joinPoint.getSignature().getName() + “ method”);
System.out.println(“******”);
}

@After(“execution(* com.praveen.aop.service.EmployeeService.addEmployee())”)
public void logAfter(JoinPoint joinPoint)
{

System.out.print(“logAfter() is running!”);
System.out.println(“, after “
+ joinPoint.getSignature().getName() + “ method”);
System.out.println(“******”);
}

@AfterReturning(
pointcut = “execution(* com.praveen.aop.service.EmployeeService.addEmployee())”,
returning = “result”)
public void logAfterReturning(JoinPoint joinPoint, Object result)
{

System.out.print(“logAfterReturning() is running!”);
System.out.println(“, after “
+ joinPoint.getSignature().getName() + “ method”);
System.out.println(“Method returned value is = “ + result);
System.out.println(“******”);
}

@AfterThrowing(
pointcut = “execution(* com.praveen.aop.service.EmployeeService.addEmployee())”,
throwing = “exception”)
public void logAfterThrowing(JoinPoint joinPoint,
Throwable exception)
{

System.out.print(“logAfterThrowing() is running!”);
System.out.println(
“, after “ + joinPoint.getSignature().getName()
+ “ method throwing exception”);
System.out.println(“exception = “ + exception);
System.out.println(“******”);
}

}

package com.praveen.aop.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.praveen.aop.config.EmployeeConfig;
import com.praveen.aop.service.EmployeeService;

public class EmployeeTest {

private static ApplicationContext context;

public static void main(String args[]) {

context = new AnnotationConfigApplicationContext(EmployeeConfig.class);

System.out.println(“ — — — — — — — — — — — — — — — — — — — -”);

EmployeeService employeeService = context
.getBean(EmployeeService.class);

employeeService.addEmployee();

employeeService.modifyEmployee();

employeeService.deleteEmployee();
}

}

Output:

Aug 05, 2019 9:28:06 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh

INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4ccabbaa: startup date [Mon Aug 05 21:28:06 IST 2019]; root of context hierarchy
Aug 05, 2019 9:28:06 PM org.springframework.beans.factory.support.DefaultListableBeanFactory registerBeanDefinition
INFO: Overriding bean definition for bean ‘employeeService’ with a different definition: replacing [Generic bean: class [com.praveen.aop.service.EmployeeService]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\Praveen\Projects\workspace\praveenspringproject\target\classes\com\praveen\aop\service\EmployeeService.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=employeeConfig; factoryMethodName=getBean; initMethodName=null; destroyMethodName=(inferred); defined in com.praveen.aop.config.EmployeeConfig]
— — — — — — — — — — — — — — — — — — — -
logBefore() is running!, before addEmployee method
******
Exception in thread “main” java.lang.NullPointerException
at com.praveen.aop.service.EmployeeService.addEmployee(EmployeeService.java:12)
at com.praveen.aop.service.EmployeeService$$FastClassBySpringCGLIB$$e07f223e.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:747)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
at com.praveen.aop.service.EmployeeService$$EnhancerBySpringCGLIB$$90908b90.addEmployee(<generated>)
at com.praveen.aop.test.EmployeeTest.main(EmployeeTest.java:22)
Add Employee
logAfter() is running!, after addEmployee method
******
logAfterThrowing() is running!, after addEmployee method throwing exception
exception = java.lang.NullPointerException
******

For example if we have commented the exception in EmployeeService

package com.praveen.aop.service;

import org.springframework.stereotype.Component;

@Component
public class EmployeeService
{
public String addEmployee()
{
System.out.println(“Add Employee “);
/*
* String name = null; name.toLowerCase();
*/
return “Employee Peter information is added successfully”;
}

public void modifyEmployee()
{
System.out.println(“Modify Employee”);
}

public void deleteEmployee()
{
System.out.println(“Delete Employee”);
}

}

package com.praveen.aop.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

@Aspect
@Component
@EnableAspectJAutoProxy
public class LoggingAspect
{

@Before(“execution(* com.praveen.aop.service.EmployeeService.addEmployee())”)
public void logBefore(JoinPoint joinPoint)
{

System.out.print(“logBefore() is running!”);
System.out.println(“, before “
+ joinPoint.getSignature().getName() + “ method”);
System.out.println(“******”);
}

@After(“execution(* com.praveen.aop.service.EmployeeService.addEmployee())”)
public void logAfter(JoinPoint joinPoint)
{

System.out.print(“logAfter() is running!”);
System.out.println(“, after “
+ joinPoint.getSignature().getName() + “ method”);
System.out.println(“******”);
}

@AfterReturning(
pointcut = “execution(* com.praveen.aop.service.EmployeeService.addEmployee())”,
returning = “result”)
public void logAfterReturning(JoinPoint joinPoint, Object result)
{

System.out.print(“logAfterReturning() is running!”);
System.out.println(“, after “
+ joinPoint.getSignature().getName() + “ method”);
System.out.println(“Method returned value is = “ + result);
System.out.println(“******”);
}

@AfterThrowing(
pointcut = “execution(* com.praveen.aop.service.EmployeeService.addEmployee())”,
throwing = “exception”)
public void logAfterThrowing(JoinPoint joinPoint,
Throwable exception)
{

System.out.print(“logAfterThrowing() is running!”);
System.out.println(
“, after “ + joinPoint.getSignature().getName()
+ “ method throwing exception”);
System.out.println(“exception = “ + exception);
System.out.println(“******”);
}

@Pointcut(“execution(* com.praveen.aop.service.EmployeeService.modifyEmployee())”)
public void modifyEmployee() {}

@Around(“modifyEmployee()”)
public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(“Around Advice Initial message”);
pjp.proceed();
System.out.println(“Around Advice later message”);

}

}

Aug 05, 2019 9:54:49 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4ccabbaa: startup date [Mon Aug 05 21:54:49 IST 2019]; root of context hierarchy
Aug 05, 2019 9:54:50 PM org.springframework.beans.factory.support.DefaultListableBeanFactory registerBeanDefinition
INFO: Overriding bean definition for bean ‘employeeService’ with a different definition: replacing [Generic bean: class [com.praveen.aop.service.EmployeeService]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\Praveen\Projects\workspace\praveenspringproject\target\classes\com\praveen\aop\service\EmployeeService.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=employeeConfig; factoryMethodName=getBean; initMethodName=null; destroyMethodName=(inferred); defined in com.praveen.aop.config.EmployeeConfig]
— — — — — — — — — — — — — — — — — — — -
logBefore() is running!, before addEmployee method
******
Add Employee
logAfter() is running!, after addEmployee method
******
logAfterReturning() is running!, after addEmployee method
Method returned value is = Employee Peter information is added successfully
******
Around Advice Initial message
Modify Employee
Around Advice later message
Delete Employee

Example for @Around for ExecutionTimeTrackerAdvice in Spring MVC project

package com.praveen.aop.advice;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

@EnableAspectJAutoProxy
@Aspect
@Component
public class ExecutionTimeTrackerAdvice {

Logger logger = Logger.getLogger(ExecutionTimeTrackerAdvice.class);

@Around(“@annotation(com.praveen.aop.advice.TrackExecutionTime)”)
public Object trackTime(ProceedingJoinPoint pjp) throws Throwable {
long stratTime = System.currentTimeMillis();
Object obj = pjp.proceed();
long endTime = System.currentTimeMillis();
logger.info(“Method name” + pjp.getSignature() + “ time taken to execute : “ + (endTime — stratTime));
return obj;
}

}

package com.praveen.aop.advice;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TrackExecutionTime {

}

package com.praveen.controller;

import java.util.Locale;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.praveen.aop.advice.TrackExecutionTime;

@Controller
public class AddController {

@GetMapping(“/index”)
@TrackExecutionTime
public String homeInit(Locale locale, Model model) {
return “index”;
}

@RequestMapping(value = “/process”, params = “add”)
@TrackExecutionTime
public ModelAndView add(@RequestParam(“num1”) int num1, @RequestParam(“num2”) int num2) {
int result = num1 + num2;
ModelAndView mv = new ModelAndView();
mv.setViewName(“display”);
mv.addObject(“result”, result);
return mv;
}

@RequestMapping(value = “/process”, params = “duplicate”)
@TrackExecutionTime
public @ResponseBody ModelAndView validateNumbers(@RequestParam(“num1”) int num1, @RequestParam(“num2”) int num2) {
ModelAndView mv = new ModelAndView();
if (num1 == num2) {
mv.setViewName(“error”);
} else {
int result = num1 + num2;
mv.setViewName(“display”);
mv.addObject(“result”, result);
}
return mv;
}

}

Output:

Aug 05, 2019 9:35:32 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 2152 ms
Aug 05, 2019 9:35:38 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 5669 ms
2019–08–05 21:35:45 INFO ExecutionTimeTrackerAdvice:22 — Method nameString com.praveen.controller.AddController.homeInit(Locale,Model) time taken to execute : 31
2019–08–05 21:35:53 INFO ExecutionTimeTrackerAdvice:22 — Method nameModelAndView com.praveen.controller.AddController.add(int,int) time taken to execute : 0
2019–08–05 21:35:57 INFO ExecutionTimeTrackerAdvice:22 — Method nameModelAndView com.praveen.controller.AddController.validateNumbers(int,int) time taken to execute : 0
2019–08–05 21:36:02 INFO ExecutionTimeTrackerAdvice:22 — Method nameModelAndView com.praveen.controller.AddController.validateNumbers(int,int) time taken to execute : 0

I am a software programmer which brings passion of learning new technologies.