Spring AOP

While developing any typical software application, we see that our application code  is littered with codes that have nothing to do with the business logic. It includes code written for logging, transaction, security etc. These are functions that are not part of the business problem but they span all throughout the application. Spring AOP helps to address this issue by modularizing these cross-cutting concerns into separate classes called as aspects.


AOP Jargons

AdviceThe job that has to be performed is called as advice. It defines what has to be done and when it has to be done.

Join pointsThese are all possible points in the program execution where the aspect can be applied. For example, at some point where a method is called, an exception is thrown, a field is modified etc. It defines all possible points where the job can be done.

PointcutsThese are the actual points where the aspect is applied. It is a subset of the join points. It defines where the job has to be done.

Aspects: An aspect is a merger of advice and pointcuts. It defines everything about the job, what has to be done, when it has to be done and where it has to be done.

WeavingThe process of applying the aspect is called as weaving. In Spring AOP aspects are woven at runtime.

Note: Spring’s AOP support is proxy based, which means aspects are woven into spring beans by wrapping them into proxies. This limits Spring AOP support to only method join points.

Defining Pointcuts

In Spring, pointcuts are defined using AspectJ expression language. Consider that we want to apply an advice on execution of the below method 'myMethod' of class AOPExample of package cubearticle.example.aop:

public String myMethod(String arg1, String arg2){
}

We can use the pointcut expression shown below:

execution(String cubearticle.example.aop.AOPExample.myMethod(String, String))

To match all methods of the class AOPExample, we can use the expression below. Note the use of asterisk(*) to match all methods.

execution(String cubearticle.example.aop.AOPExample.*(String, String))

But the above expression would match only if all the method of AOPExample has two String parameters and returns a String value. But this is a rare consideration. If we want to match any number of parameters and all return types, the below expression matches our requirement. We use a double dot (..) to match any number of parameters and asterisk (*) to match any return type.

execution(* cubearticle.example.aop.AOPExample.*(..))

We might also want to match all classes inside a particular package. The below expression match all methods inside any classes of package cubearticle.example and its sub packages.

execution(* cubearticle.example..*(..))

Sample Code

To demonstrate Spring AOP, we will use a simple logging example. Logging is not a part of the business logic and hence the business classes should not be responsible to do logging. We will take the example of a simple Calculator class and a CalculatorLogger that will serve as the aspect. It will log messages automatically when a method is called, any exception is thrown or we return from a method.

The Calculator class is shown below. It's a simple class that defines two methods, add and divide. Note that it is annotated with @Component since we want it to be managed as a spring bean.

package cubearticle.example.spring.aop;

import org.springframework.stereotype.Component;

@Component
public class Calculator {
    
    public Integer add(int a, int b){
        int result = a+b;
        return result;
    }
    
    public Integer divide(int a, int b) throws ArithmeticException{
        if(b ==0){
            throw new ArithmeticException("Cant divide a number by zero");
        }
        int result = a / b;
        return result;
    }
}

Aspect

Shown next is our aspect, CalculatorLogger. It is annotated with @Aspect to indicate Spring that it is an aspect. This class is responsible for logging messages when any method of Calculator class is executed.

package cubearticle.example.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class CalculatorLogger {
    
    @Before("execution(* cubearticle.example.spring.aop.Calculator..*(..))")
    public void doLogBefore(JoinPoint jp){
        String className = jp.getTarget().getClass().toString();
        String methodName = jp.getSignature().toString().
                substring(jp.getSignature().toString().lastIndexOf(".")+1);
        String args="";
        System.out.println("");
        for(Object obj: jp.getArgs()){
            args = args+ obj.toString()+ ", ";
        }
        System.out.println("Entering into method "+ methodName +
                " of " + className+ " with params :"+ args);
    }
    
    @AfterReturning("execution(* cubearticle.example.spring.aop.Calculator..*(..))")
    public void doLogAfter(JoinPoint jp){
        String className = jp.getTarget().getClass().toString();
        String methodName = jp.getSignature().toString().
                substring(jp.getSignature().toString().lastIndexOf(".")+1);
        System.out.println("Returning from method "+ methodName +" of " + className);
    }
    
    @AfterThrowing(pointcut="execution(* cubearticle.example.spring.aop.Calculator..*(..))",
            throwing="e")
    public void doLogException(JoinPoint jp, Throwable e){
        System.out.println("Exception occured "+ e);
    }
}

Notice that the methods in our aspect are annotated with advice annotations to indicate when these methods should be called. Below are the list of available advice annotations and their purpose.

  • @After - Called after return from advised method or when an exception is thrown
  • @Before - Called before the advised method execution
  • @AfterReturning - Called after return from advised method
  • @AfterThrowing - Called after an exception is thrown from the advised method
  • @Around - It wraps the advised method. Can be used as a replacement for all the above annotations.

Spring Configuration

Before our aspect could be used in our application, Spring should be able to interpret it as an aspect and create proxies for it. To do so we need to enable auto-proxying in our Spring configuration and declare our aspect as a Spring bean. Spring provides @EnableAspectJAutoProxy annotation to enable auto-proxying. The Spring Java Config class is shown below:

package cubearticle.example.spring.aop;

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

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class Config {
    
    @Bean
    public CalculatorLogger calculatorLogger(){
        return new CalculatorLogger();
    }
}

Main Class

Shown below is the CalculatorMain class, where we create Spring application context and call the methods of the calculator class:

package cubearticle.example.spring.aop;

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

public class CalculatorMain {
    
    private static ApplicationContext cntx;

    public static void main(String[] args) {
        cntx = new AnnotationConfigApplicationContext(Config.class);
        Calculator calc = (Calculator)cntx.getBean("calculator");
        calc.add(2, 4);
        calc.divide(5, 0);
    }
}

Below is the output on executing the CalculatorMain:

Entering into method add(int,int) of class cubearticle.example.spring.aop.Calculator with params :2, 4, 
Returning from method add(int,int) of class cubearticle.example.spring.aop.Calculator

Entering into method divide(int,int) of class cubearticle.example.spring.aop.Calculator with params :5, 0, 
Exception occured java.lang.ArithmeticException: Cant divide a number by zero

Using @Around

An around advice can be used to wrap the entire method. We can write a single method that can work as before as well as after advice. Shown below is a new aspect called CalculatorLoggerAroundAdvice.

package cubearticle.example.spring.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class CalculatorLoggerAroundAdvice {

    @Pointcut("execution(Integer cubearticle.example.spring.aop.Calculator.*(..))")
    public void log(){}
    
    @Around("log()")
    public void doLog(ProceedingJoinPoint jp){
        String className = jp.getTarget().getClass().toString();
        String methodName = jp.getSignature().toString().
substring(jp.getSignature().toString().lastIndexOf(".")+1); String args=""; System.out.println(""); for(Object obj: jp.getArgs()){ args = args+ obj.toString()+ ", "; } try{ System.out.println("Entering into method "+ methodName + " of " + className+ " with params :"+ args); Object val = jp.proceed(); System.out.println("Returning from method "+ methodName + " of " + className+ " with return val :"+ val); }catch(Throwable e){ System.out.println("Exception occured: "+ e); } } }

The around advice method is provided with a ProceedingJoinPoint object, which is later used to call the advised method using proceed() method. Before advice code can be written before calling jp.proceed() method, similarly we can write the after advice code after jp.proceed() is called. The catch block acts as an afterthrowing advice.

Also notice the use of @Pointcut, this is called as a named pointcut where we declare a pointcut and use it with our advice annotation.


Download Source Code

You can download the source code of the examples discussed in this article.

POPULAR ARTICLES

Creating Conditional Beans in Spring

The concept of condition beans enables Spring to restrict the creation of any bean depending on the evaluation of a condition. These beans get created only when a preset condition is evaluated as true

View Article

Accepting Request Param and Path Variable in Spring Controller

Spring MVC provides various ways through which a client browser can pass data to the Controller. In this article we will discuss about accepting Request Parameters and Path Variables in Spring Contr..

View Article

Generate Namespace & Schema Information using JAXB

Most xml documents used in enterprise applications makes use of namespace to avoid element name conflicts. This article talks about generating these namespace and schema information when marshaling...

View Article

Switching Database Profile using Spring Profiles

We are most likely to have separate db configuration for different environment like development and production environment. Spring profiles provide a convenient way to switch db profiles at runtime.

View Article

SQL and its Sub-Languages

SQL (Structured Query Language) is a language understood by most modern databases. It is an ANSI (American National Standard Institute) standard language which is used to manipulate databases.

View Article

Introducing JUnit Rule

Junit Rules allows developers to add additional functionalities that can applied to all test methods in a test class. It is similar to the concept of custom test runners but with reduced restrictions.

View Article

Addressing Ambiguity in Spring Autowiring

Spring autowiring is powerful concept, but we should be very cautious while using it. We may end up in creating ambiguity while autowiring beans, which will cause autowiring to fail.

View Article

Creating and Using Synonym in Oracle Database

Synonyms are database objects used to provide duplicate names to existing objects in the database. It is just an alternate name used to hide the original name of the object.

View Article

Creating and Using Sequence in Oracle Database

A sequence is used to auto-generate numbers in ascending or descending order which can serve as a primary key or a part of it (in case of composite key).

View Article

Creating and Manipulating Constraints in Oracle Database

Constraints are used to impose certain rules on columns to avoid invalid data entry into the table. If any of the constraint is violated the operation fails.

View Article

Integrating Log4J with Perf4J for Performance Logging

Perf4j is an open source logging framework used primarily for monitoring performance statistics in java applications. Log4j has the ability to integrate with perf4j to capture performance data.

View Article

Tagging in GIT

Tagging allows us to mark a specific point in the commit history or snapshot. A tag is typically used to mark a project release. This article shows how to create tags in Git.

View Article