Skip to content

Spring AOP

AOP stands for Aspect-Oriented Programming and it helps decouple cross-cutting concerns from the object that they affect. It’s similar to DI in the sense that DI helps decouple an application’s object from each other.

According to the Spring,

Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns (such as transaction management) that cut across multiple types and objects. (Such concerns are often termed "crosscutting" concerns in AOP literature.)

One of the key components of Spring is the AOP framework. While the Spring IoC container does not depend on AOP (meaning you do not need to use AOP if you don’t want to), AOP complements Spring IoC to provide a very capable middleware solution.

What Problem AOP Solve?

AOP helps in separating cross-cutting concerns from the business logic, which results in cleaner code and also helps developers to focus on building business logic.

A typical usage in Spring framework is the declarative transaction management.

  • Without AOP, we need to manage the transaction in each method that needs transaction
  • With AOP, in our business logic method, we only need to add annotation @Transactional, then Spring will help to create/manage the transaction by using AOP

AOP Concept

  • Aspect: A modularization of a concern that cuts across multiple classes. Transaction management is a good example of a crosscutting concern in enterprise Java applications. In Spring AOP, aspects are implemented by using regular classes (the schema-based approach) or regular classes annotated with the @Aspect annotation (the @AspectJ style).

  • Join point: A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.

  • Advice: Action taken by an aspect at a particular join point. Different types of advice include "around", "before", and "after" advice. (Advice types are discussed later.) Many AOP frameworks, including Spring, model an advice as an interceptor and maintain a chain of interceptors around the join point.

  • Pointcut: A predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.

  • Introduction: Declaring additional methods or fields on behalf of a type. Spring AOP lets you introduce new interfaces (and a corresponding implementation) to any advised object. For example, you could use an introduction to make a bean implement an IsModified interface, to simplify caching. (An introduction is known as an inter-type declaration in the AspectJ community.)

  • Target object: An object being advised by one or more aspects. Also referred to as the "advised object". Since Spring AOP is implemented by using runtime proxies, this object is always a proxied object.

  • AOP proxy: An object created by the AOP framework in order to implement the aspect contracts (advise method executions and so on). In the Spring Framework, an AOP proxy is a JDK dynamic proxy or a CGLIB proxy.

  • Weaving: linking aspects with other application types or objects to create an advised object. This can be done at compile time (using the AspectJ compiler, for example), load time, or at runtime. Spring AOP, like other pure Java AOP frameworks, performs weaving at runtime.

Reference https://docs.spring.io/spring-framework/reference/core/aop/introduction-defn.html

Spring AOP Advices

  • Before advice: Advice that runs before a join point but that does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).

  • After returning advice: Advice to be run after a join point completes normally (for example, if a method returns without throwing an exception).

  • After throwing advice: Advice to be run if a method exits by throwing an exception.

  • After (finally) advice: Advice to be run regardless of the means by which a join point exits (normal or exceptional return).

  • Around advice: Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.

Spring AOP Proxies

Spring AOP defaults to using standard JDK dynamic proxies for AOP proxies. This enables any interface (or set of interfaces) to be proxied.

Spring AOP can also use CGLIB proxies. This is necessary to proxy classes rather than interfaces.

Example

Java
// service interface
public interface HelloWorldService {
    String sayHello(String name);
}

// service implementation
@Service
@Slf4j
public class HelloWorldServiceImpl implements HelloWorldService {
    @Override
    public String sayHello(String name) {
        log.info("hello for {}", name);
        return "Hello " + name + "!!!";
    }
}

// AOP implementation
@Aspect
@Component
@Slf4j
public class HelloWorldAOP {
    // define the pointcut
    @Pointcut("execution(public * com.exia.aop.service..*(..))")
    public void pointcut(){

    }

    @Before(value = "pointcut()")
    public void before(JoinPoint point) {
        String className = point.getTarget().getClass().getSimpleName();
        String methodName = point.getSignature().getName();
        log.info("Aspect - before invoking {}.{}", className, methodName);
    }

    @AfterReturning(value = "pointcut()", returning = "result")
    public void afterReturn(JoinPoint point, Object result) {
        if (result != null) {
            log.info("Aspect - after result: {}", result);
        } else {
            log.info("Aspect - after result: null");
        }
    }
}

// runner to run call the service
@Component
@Slf4j
public class HelloWorldRunner implements CommandLineRunner {
    private final HelloWorldService helloWorldService;

    public HelloWorldRunner(HelloWorldService helloWorldService) {
        this.helloWorldService = helloWorldService;
    }


    @Override
    public void run(String... args) {
        log.info("runner - before call");
        log.info("runner - result: {}", helloWorldService.sayHello("jun"));
        log.info("runner - after call");

    }
}

Run the above code, will get the result:

Text Only
2023-11-04T17:08:49.661+08:00  INFO 6890 --- [           main] com.exia.aop.AopApplication              : No active profile set, falling back to 1 default profile: "default"
2023-11-04T17:08:49.937+08:00  INFO 6890 --- [           main] com.exia.aop.AopApplication              : Started AopApplication in 0.401 seconds (process running for 0.627)
2023-11-04T17:08:49.938+08:00  INFO 6890 --- [           main] com.exia.aop.runner.HelloWorldRunner     : runner - before call
2023-11-04T17:08:49.940+08:00  INFO 6890 --- [           main] com.exia.aop.aop.HelloWorldAOP           : Aspect - before invoking HelloWorldServiceImpl.sayHello
2023-11-04T17:08:49.940+08:00  INFO 6890 --- [           main] c.e.a.s.impl.HelloWorldServiceImpl       : hello for jun
2023-11-04T17:08:49.940+08:00  INFO 6890 --- [           main] com.exia.aop.aop.HelloWorldAOP           : Aspect - after result: Hello jun!!!
2023-11-04T17:08:49.940+08:00  INFO 6890 --- [           main] com.exia.aop.runner.HelloWorldRunner     : runner - result: Hello jun!!!
2023-11-04T17:08:49.941+08:00  INFO 6890 --- [           main] com.exia.aop.runner.HelloWorldRunner     : runner - after call