Quick introduction

A programming paradigm is a style of programming. Some paradigms are better suited for certain types of problems as an approach to solving them. Each paradigm consists of specific structures and features for organizing a program and its parts. But, we should keep in mind that programming languages are not always tied to a particular paradigm, nor does it have to be a single paradigm. The same applies to programming techniques. A technique refers to a specific set of tools, mechanisms, and practices used to implement or apply a particular concept or approach within a given programming paradigm. 

One such paradigm or technique (depending on the advised literature) is aspect-oriented programming. Here we take a look at its abilities, the types of problems it helps us solve and how.

About AOP

Growing applications can become very complex. The way to manage this is to split the application according to its responsibilities or concerns. This type of organization allows for code reuse, encapsulation, maintainability, and much more.

Picture Source

However, layers have common concerns called cross-cutting concerns (monitoring, caching, etc.). One of the most common ways to implement them would be with aspect-oriented programming. Spring is a popular framework that implements AOP. In the following, we will focus on Spring AOP.

Terminology

There are three fundamental concepts in Aspect-Oriented Programming (AOP):

  1. Advice is the action performed by an aspect at a particular join point in the application. Join points are specific points in the program flow, such as method invocations, exception handling, or field access. There are several types of advice in AOP, including @Before@After@Around@AfterReturning, and @AfterThrowing. Each type of advice determines when an action should be taken in relation to the join point.
  2. Pointcuts are expressions that select a set of join points in the application where an aspect’s advice should be applied. Pointcut expressions can be based on method signatures, annotations, class hierarchies, etc.
  3. Inter-type declarations allow you to add new members, methods, and interfaces to an existing class at a compile time. This means that you can add new behaviors to a class without directly changing its source code.

Example

A well-known example of a cross-cutting concern is logging. It is safe to say that every application has it in some form. From API’s controller actions or middleware to the execution of specific tasks, it is a part of several layers.

Consider the following example of a class Calculator that performs arithmetic operations:

public class Calculator {
    public int add(final int a, final int b) {
        return a + b;
    }

    public int subtract(final int a, final int b) {
        return a - b;
    }
}

Now, logging each time these methods are called would require the following:

1. Creation of an aspect class that defines the behavior we want to add to the Calculator class. For example, create a logging aspect that logs every method call:

@Aspect
public class LoggingAspect {

    @Before("execution(* com.example.Calculator.*(..))")
    public void logMethodCall(final JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Method " + methodName + " called");
    }
}

The @Aspect annotation indicates to Spring that this class contains advice, pointcuts, and/or inter-type declarations that can be used to modularize cross-cutting concerns in an application. This signals Spring that the AOP framework should process this class, and its advice should be applied to the appropriate join points.

In addition to this annotation, Spring should be configured to use AOP, either by enabling the AspectJ proxy or by using Spring’s proxy-based AOP support. Once configured, Spring creates proxies around objects and applies the advice from aspects at the appropriate joint points in the program flow.

This aspect uses the @Before advice to log the method call before it occurs. The execution(* com.example.Calculator.*(..)) expression specifies the pointcut for which methods to apply this advice to. com.example would be the package of the class.

2. Configuration of aspects in Spring [1]. For example, we can create a configuration class that defines the Calculator bean and the LoggingAspect bean:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

    @Bean
    public Calculator calculator() {
        return new Calculator();
    }

    @Bean
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
}

The @EnableAspectJAutoProxy annotation enables Spring to use AspectJ to apply AOP. The calculator() method defines the Calculator bean, and the loggingAspect() method defines the LoggingAspect bean.

[1] Used: Java 20 and Spring Boot 3.1.0 (with Spring Framework 6.0.9)

3. Usage of the Calculator bean. For example, the Main class that uses the Calculator to perform some operations:

public class Main {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Calculator calculator = context.getBean(Calculator.class);
        int result = calculator.add(2, 3);
        System.out.println(result);
        context.close();
    }
}

After running the Main class, the following output will be provided: 

Method add called
5

As can be seen, the LoggingAspect logged the method call before the add() method was called. This demonstrates the use of AOP to add behaviors to classes without directly modifying them.

With this AOP, we were able to modularize a cross-cutting concern into a separate aspect that could then be applied to the appropriate components or modules through pointcut expressions. This makes managing and maintaining cross-cutting concerns easier since they are encapsulated in separate aspects that can be reused across the codebase.

Conclusion

AOP is a programming paradigm/technique that aims to increase modularity by allowing the separation of cross-cutting concerns. It is very powerful and must be handled carefully. Otherwise, it can create difficult situations to track down because it can introduce hidden behavior into the codebase, making it difficult to understand and debug. It’s important to carefully evaluate which parts of the codebase would truly benefit from AOP and avoid using it unnecessarily.


“AOP (Aspect-Oriented Programming) with Spring” Tech Bite was brought to you by Sara Zaimović, Junior Software Engineer at Atlantbh.

Tech Bites are tips, tricks, snippets or explanations about various programming technologies and paradigms, which can help engineers with their everyday job.

oban
Software DevelopmentTech Bites
February 23, 2024

Background Jobs in Elixir – Oban

When and why do we need background jobs? Nowadays, background job processing is indispensable in the world of web development. The need for background jobs stems from the fact that synchronous execution of time-consuming and resource-intensive tasks would heavily impact an application's  performance and user experience.  Even though Elixir is…

Want to discuss this in relation to your project? Get in touch:

Leave a Reply