How to Build Apps with Spring AOP

Spring AOP gives us a cleaner approach to write code. It helps in segregating business logic and cross-cutting stuff. Some of the cross-cutting stuff are logging, security management, transaction management, etc.

By using AOP, we could also add more functionality to methods in a class, even though the source code is not available.

Here, we are not going to discuss the basics of AOP. The expectation is that readers are well aware of the basic concepts of AOP like advice, aspect, pointcut, etc. We will focus mainly on Spring AOP applications.

In any application that we develop, we define business logic in the business layer, and to execute the business logic, the business layer collaborates with the repository/data access layer. And then, the repository layer interacts with the database for any data.

A typical example of business logic and repository layer are as follows:

@Component
public class BusinessLogic {
 @Autowired
 Repository Config repositoryConfig;
 @Autowired
 Repository A repositoryA;
 public void write(Repository repository) {

 switch (repository) {

  case REPOSITORYA:

  repositoryConfig.writeToCollectionA();

  break;

  caseREPOSITORYB:

   repositoryA.writeToCollectionB();

  break;

 }

 }

 public void read(Repository repository) {

 switch (repository) {

  caseREPOSITORYA:

  repositoryConfig.readFromCollectionA();

  break;

  caseREPOSITORYB:

  repositoryA.readFromCollectionB();

  break;

 }

 }
}

@Component

public class RepositoryA {

 public void readFromCollectionB() {

 System.out.println(“Reading from collectionA”);

 }

 public void writeToCollectionB() {

 System.out.println(“Writing to collectionA”);

 }

}

@Component

public class RepositoryConfig {

 public void readFromCollectionA() {

 System.out.println(“Reading from collection Config”);

 }

 public void writeToCollectionA() {

 System.out.println(“Writing to collection Config”);

 }

}

In the above example, we have a BusinessLogic class in the Business Layer, and it interacts with two Repository Layer classes: RepositoryA and RepositoryConfig.

In our Repositories classes, we have added a dummy implementation, but in the real world, we will write code to interact with the database.

Let’s assume the following scenario.

In the first release of our application, we didn’t have the authorization functionality, but in the subsequent release, we have added authorization functionality. Now, we have a change request that only admin users should be allowed to read or write from the configuration collection.

In our above example, the RepositoryConfig class interacts with the Configuration collection. So, now, we need to add a check if the user is admin before it queries the Configuration collection.

One way to implement this functionality is to create an Authorization class and isAuthorized method to check whether the user is authorized or not. We update all the methods of RepositoryConfig and check authorization by calling isAuthorized method.

@Component

public class RepositoryConfig {

public void readFromCollectionA() {

if (Authorization.isAuthorised(Session.getUserId())) {

System.out.println(“Reading from collectionA”);

}

}

public void writeToCollectionA() {

if (Authorization.isAuthorised(Session.getUserId())) {

System.out.println(“Writing to collectionA”);

}

}

}

However, there is a disadvantage to this approach. We need to modify each and every method of RepositoryConfig class and the same code of calling the isAuthorized method is repeated across all the methods to check the authorization.

It will be good if without changing the existing class, we can add the authorization check functionality.

Let’s see how we can leverage the power of AOP to achieve this.

We will define an AuthorizationCheck class where we mention the aspect. We create a pointcut to the RepositoryConfig class and advice type is around. In the advice, we check if the user is admin, then we proceed; otherwise, we don’t.

There is no need to make any changes in the RepositoryConfig class. 

The code is mentioned below.

@Aspect

@Configuration

public class AuthorizationCheck {

 @Around("execution(** com.springboot.tutorial.aop.repository.RepositoryConfig.*(..))")

 public void check (ProceedingJoinPoint jp) {

      try {

           if(Authorization.isAuthorised(Session.getUserId())) {

                 jp.proceed();

           }

           else {

                 System.out.println("User not Authorised");

           }

      } catch (Throwable e) {

                e.printStackTrace();

      }

 }

}

Now, for any call to any of the methods of the RepositoryConfig class, this aspect will get called first, only if the condition satisfies the actual method will get called.

As you see above, without changing the existing class, we have added functionality. Using the same concept, we can even extend the functionality when we don’t have the source code.

Another useful application of AOP is that we can add functionality in a controlled manner with help of annotations.

Let’s consider a scenario where we are investigating a performance issue, and for that, we randomly check the turnaround time of a method.

AOP combined with annotations can make things very simple. Let’s create a custom annotation TurnAroundTime and apply it to the methods on which we want to check the performance.

Below is an example:

First, we need to create an annotation.

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public@interface TurnAroundTime {

}

We want to check the performance of a method, so here, the target type is Method. RetentionPolicy is runtime, as we want to apply during run time.

Next, we define the aspect.

@Aspect

@Configuration

public class PerformanceCheck {

@Around("execution(** com.springboot.tutorial.aop...*(…)) &&

@annotation(TurnAroundTime)")

public void watchPerformance(ProceedingJoinPoint jp) {

Instant start = Instant.now();

try {

jp.proceed();

} catch (Throwable e) {

e.printStackTrace();

}

Instant finish = Instant.now();

long timeElapsed = Duration.between(start, finish).toMillis();

System.out.println(“Total Turnaround time:” + timeElapsed);

}

}

We define an aspect with pointcut as our custom annotation and advice type is around. We measure the difference between the time before the method execution begins and after the control returns and log the difference.

So, whenever we want to measure the performance, we need to annotate the method with the custom annotation @TurnAroundTime.

Something like this:

@Component

public class BusinessLogic {

@Autowired

 RepositoryConfig repositoryConfig;

@Autowired

 RepositoryA repositoryA;



 @TurnAroundTime

 public void write(Repository repository) {

      switch(repository){

      caseREPOSITORYA:

           repositoryConfig.writeToCollectionA();

           break;

      caseREPOSITORYB:

           repositoryA.writeToCollectionB();

           break;


                
      }

 }

}

The turnaround time of the method will be logged.

As we see, there are a few disadvantages using Spring AOP. If a new developer joins the team and he/she is not well versed with Spring AOP, sometimes, it becomes a bit confusing to debug. It is recommended to have good documentation practice.

Thanks for reading! Originally published on https://dzone.com

#spring-boot #mobile-apps #web-development

How to Build Apps with Spring AOP
2 Likes10.50 GEEK