Saturday, December 22, 2018

AOP with AspectJ in Spring Boot

In my previous blog post I have created Spring Boot REST API for simple CRUD operation and integrated junit unit tests for it. In this post I'm demonstrating how to use AspectJ for implement AOP in the REST API.

Prerequisites 


  1. You should have install java 1.8 or above.
  2. You should have Eclipse installed in your PC.
  3. Your PC should setup Maven installed and configured.
  4. You should have REST API project to implement AOP on top of it. I'm using REST api which I developed on previous post. (You can checkout previous code from here)

What is Aspect Orient Programing 

Aspect Orient Programming or AOP is a programming technique that aims to increase modularity by allowing the separation of cross-cutting concerns such as transaction management, logging etc. It does so by adding additional behavior to existing code without modification of the code itself. Instead, we declare separately which code is to modify.

AspectJ is currently the most complete implementation of an AOP language for Java. In practical terms, it is an extension to Java that treats AOP concepts as first-class elements of the language. It allows aspects to be implemented as part of a Java based application using familiar Eclipse based tools.

AOP concepts 

Before we go in to programming on AOP we have to have idea about terms which we use on AOP.


  1. Aspect: An aspect is a class that implements enterprise application concerns that cut across multiple classes, such as transaction management, logging etc. Aspects is a normal class configured through Spring XML configuration or we can use Spring AspectJ integration to define a class as Aspect using @Aspect annotation.
  2. Join Point: A join point is the specific point in the application such as method execution, exception handling, changing object variable values etc. In Spring AOP a join points is always the execution of a method.
  3. Advice: Advices are actions taken for a particular join point. In terms of programming, they are methods that gets executed when a certain join point with matching pointcut is reached in the application. You can think of Advices as Struts2 interceptors or Servlet Filters.
  4. Pointcut: Pointcut are expressions that is matched with join points to determine whether advice needs to be executed or not. Pointcut uses different kinds of expressions that are matched with the join points and Spring framework uses the AspectJ pointcut expression language.
  5. Target Object: They are the object on which advices are applied. Spring AOP is implemented using runtime proxies so this object is always a proxied object. What is means is that a subclass is created at runtime where the target method is overridden and advices are included based on their configuration.
  6. AOP proxy: Spring AOP implementation uses JDK dynamic proxy to create the Proxy classes with target classes and advice invocations, these are called AOP proxy classes. We can also use CGLIB proxy by adding it as the dependency in the Spring AOP project.
  7. Weaving: It is the process of linking aspects with other objects to create the advised proxy objects. This can be done at compile time, load time or at runtime. Spring AOP performs weaving at the runtime.

Spring Boot dependency 

I have checkout my previous project from GIT repository and then add the dependency in to POM file related to the AOP programming. All required dependencies comes under the spring-boot-starter-test. Just need to add following part on the existing pom file.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>


Lets define our Aspect 

In AspectJ we can define the aspect as a java class with @Aspect annotation. I have created new class "MyAspect" in "com.nirmal.springbootrest.aop" package. I will demonstrate following features in AspectJ in this tutorial. 

  • @Before
  • @After
  • @AfterReturning
  • @Around

I will here add the whole implementation of the MyAspect class and latter I will describe each method in more.



package com.nirmal.springbootrest.aop;

import org.slf4j.Logger;
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.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

@Aspect
@Configuration
public class MyAspect {

private Logger logger = LoggerFactory.getLogger(this.getClass());

/**
* Before execution on
* @param joinPoint
*/
@Before("execution(* com.nirmal.springbootrest.service.*.*(..))")
public void before(JoinPoint joinPoint) {
logger.info("MyAspect Before : execution for {}", joinPoint);
}

@AfterReturning(value = "execution(* com.nirmal.springbootrest.service.*.saveOrUpdateBook(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
logger.info("MyAspect AfterReturning : returned with value {}", joinPoint, result);
}

@After(value = "execution(* com.nirmal.springbootrest.service.*.deleteBook(..))")
public void after(JoinPoint joinPoint) {
logger.info("MyAspect After : execution for {}", joinPoint);
}
@Around("@annotation(com.nirmal.springbootrest.aop.TimeTrack)")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        joinPoint.proceed();
        long timeTaken = System.currentTimeMillis() - startTime;
        logger.info("MyAspect Around : execution for {}", joinPoint, timeTaken);
    }
}



@Before 

I have created method before with @Before  annotation in my aspect class. in there I mention execution point as "com.nirmal.springbootrest.service.*.*(..))". So this before method will execute every method under the package  "com.nirmal.springbootrest.service".

First of all lets start our application then we can test it by calling our get method by using following command in command line.

curl -X GET http://localhost:8080/findBook/9999

Then you can see the following output on your console.



@AfterReturning  

I have created method AfterReturning with @AfterReturning annotation in my aspect class. in there I mention execution point as "* com.nirmal.springbootrest.service.*.saveOrUpdateBook(..))". So this method will execute every method saveOrUpdateBook under the package  "com.nirmal.springbootrest.service" if method successfully executed.

we can test it by calling our get method by using following command in command line.

curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d "{ \"isbmNumber\":\"9999\", \"name\":\"How to develop API\", \"description\":\"sample book\", \"auther\":\"Nirmal Balasooriya \" }" http://localhost:8080/saveOrUpdate

Then you can see similar output for following 



Actually in here two joint points found in our Aspect one in for before and other one is for AfterReturning.


@After  

I have created method After with @After annotation in my aspect class. in there I mention execution point as  "* com.nirmal.springbootrest.service.*.deleteBook(..))". So this after method will execute every method deleteBook under the package  "com.nirmal.springbootrest.service".

we can test it by calling our get method by using following command in command line.

curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d "{ \"isbm\":\"9999\" }" http://localhost:8080/deleteBook

Then you can see similar output for following (Please note that since I have used same deleteBook method to simulate around annotation, you can see that method also executed)




@Around

For this we create Time Track annotation class in our project with following contents

package com.nirmal.springbootrest.aop;

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 TimeTrack {

}

And then I have added @TimeTrack annotation on the deleteBook class. Then I have created method around with @Around annotation in my aspect class. in there I mention execution point as  @annotation(com.nirmal.springbootrest.aop.TimeTrack). So this around method will execute every method which has TimeTrack annotation.

we can test it by calling our get method by using following command in command line.

curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d "{ \"isbm\":\"9999\" }" http://localhost:8080/deleteBook

Then you can see similar output for following same as previous 



This will cover basic demonstration about AspectJ AOP with spring boot. You will be able to access the source code on following github location.



No comments:

Post a Comment