In a medium to large codebase, many-a-time, you might notice code that shouldn’t be in some class/method by design but is necessary. This can be anything from simple logging to transaction handling. As the codebase scales, this keeps adding up to a point where the code becomes laborious to manage.

codebase on fire!

Aspect-Oriented Programming provides a way to modularize these kinds of cross-cutting concerns with Aspects. Here is a basic example,

@Aspect
class LoggingAspect { // Aspect
    Logger LOGGER = Logger.getLogger(LoggingAspect.class.getName());

    @After("execution(* *(..))") // Pointcut
    void logExecutionAdvice(JoinPoint joinPoint) { // Advice
        LOGGER.info(joinPoint.getSignature().getName() + " executed");
    }
}

Aspect

An Aspect is a special class that contains the cross-cutting concerns. In Java, this can be created using annotations, like as above, or by using the non-java aspect format.

Pointcut

A pointcut is a special regex-like string that is used to specify where to inject your code. This can be any point in the program’s execution (a.k.a the joinpoint). A Joinpoint could be a method-body, a method-call, an exception handler, a class, an object, etc.

Advice

An advice contains the cross-cutting logic. Based on when to execute, it can be one of the below:

Advice TypeDescription
BeforeRuns before the joinpoint’s execution.
AfterRuns after the joinpoint’s execution.
AfterReturningRuns after the successful execution of the joinpoint.
AfterThrowingRuns after the joinpoint if it throws an exception.
AroundGives the before and after control to the advice.

Here is a more complex example that you might see in a production environment:

@Aspect
class TransactionAspect {
    @Around("@annotation(com.example.Transaction)") // Injects to joinpoint with @Transaction annotation
    public Object transactionAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        try {
            TransactionManager.begin();
            Object result = joinPoint.proceed(); // execution returns to the joinpoint
            TransactionManager.commit();
        } catch (Exception e) {
            TransactionManager.rollback();
            throw e;
        }

        return result;
    }
}

The above example runs the @Transaction annotated methods in a transaction.

TransactionAspect working

Weaving

Weaving is the process of injecting cross-cutting concerns into joinpoints. Based on when it is done, it is classified into of the following:

Weaving TypeDescription
Runtime WeavingThe advice is injected at runtime. Frameworks like Spring AOP use proxies to achieve this and are comparatively slower.
Compile Time WeavingThe advice is injected during compilation. Frameworks like AspectJ have their own compiler to achieve this.
Binary WeavingThe injection is done after compilation, i.e., into class/jar files.
Load Time WeavingThe injection is done when the classes are loaded by the classloader. Frameworks like AspectJ use a javaagent to achieve this.

Hope this gave you a basic idea of what AOP can do and all its terminologies. So long!

References