Use of spring aop

Usage of spring aop

1. Why use spring aop

The benefits of object-oriented programming (OOP) are obvious, and the disadvantages are equally obvious. When you need to add a common method to multiple objects that do not have an inheritance relationship, such as logging, performance monitoring, etc., if you use object-oriented programming, you need to add the same method to each object, which results in It results in a larger workload of repeated work and a large amount of repeated code, which is not conducive to maintenance. Aspect-oriented programming (AOP) is a supplement to object-oriented programming. Simply put, it is a programming idea that uniformly handles a certain “aspect” problem. If you use AOP for log recording and processing, all the logging code is concentrated in one place, and there is no need to add it to each method, which greatly reduces duplicate code.

2. Contents of spring aop

Notification (Advice) contains cross-cutting behaviors that need to be used for multiple application objects. It doesn’t matter if you don’t understand it at all. In layman’s terms, it defines “when” and “what to do”.

Join Points are all points during program execution where notifications can be applied.

Pointcut (Poincut) defines “where” to cut in and which connection points will be notified. Obviously, the tangent point must be the connection point.

Aspect (Aspect) is a combination of advice and pointcuts. Notifications and pointcuts together define what an aspect is all about-what it does, when it does it, and where it does it.

Introduction (Introduction) allows us to add new methods or attributes to existing classes.

Weaving (Weaving) is the process of applying aspects to the target object and creating a new proxy object. It is divided into compile-time weaving, class loading-time weaving and run-time weaving.

3. Summary of common annotations for spring aop

After springboot appeared, we generally used annotation programming. The following is a summary of these commonly used primary keys in aop.

  • @Aspect aspect declaration, marked on a class, interface (including annotation type) or enumeration.
    @Pointcut
    Pointcut declaration, that is, the target method of the target class to be cut into. You can use execution pointcut expressions or annotation to specify interception of methods with specified annotations.

  • The value attribute specifies the pointcut expression, the default is “”, which is used to be referenced by the notification annotation. In this way, the notification annotation only needs to be associated with this pointcut statement, and there is no need to write the pointcut expression repeatedly.

  • @Before
    Pre-notification is executed before the target method (pointcut) is executed. The value attribute is the pointcut expression of the binding notification. It can be associated with the pointcut statement or directly set the pointcut expression. Note: If an exception is thrown in this callback method, the target method will no longer be executed and will continue to execute the postscript. Notification->Exception notification.

  • @After post notification, executed after the target method (pointcut) is executed
    @AfterReturning
    Return notification, executed after the target method (pointcut) returns the result. The pointcut attribute is bound to the pointcut expression of the notification. The priority is higher than value. The default is “”

  • @AfterThrowing
    Exception notification, executed after the method throws an exception, means skipping the return notification pointcut attribute binding notification pointcut expression, the priority is higher than value, the default is””Note: If the target method itself try-catch Exception, if it does not continue to be thrown out, this callback function will not be entered.

  • @Around
    Surround notification: execute some code before and after the target method is executed, similar to an interceptor, which can control whether the target method continues to execute.

4. Use of spring aop

1. Use of @Before

We first define an aspect. Spring uses the @AspectJ annotation to annotate POJO. This annotation indicates that the class is not only a POJO, but also an aspect. Aspects are a combination of pointcuts and advices, so to define an aspect you need to write pointcuts and advices. In the code, just add the @Aspect annotation.

Then define a pointcut: the pointcut is defined through the @Pointcut annotation and pointcut expression. The @Pointcut annotation can define reusable pointcuts within an aspect. Since the minimum granularity of Spring aspects reaches the method level, execution expressions can be used to clearly specify method-related components such as method return types, class names, method names, and parameter names. In practice, most business scenarios that require the use of AOP also It only needs to reach the method level, so the execution expression is the most widely used. The figure shows the syntax of execution expression: execution means it is triggered when the method is executed. Starting with “” indicates that the method return value type is any type. Then there are the fully qualified class names and method names, “” can represent any class and any method. For the method parameter list, you can use “…” to indicate that the parameters are of any type. If you need multiple expressions, you can use “& & &”, “||” and “!” to complete AND, OR, and NOT operations.

@Component
@Aspect
public class LogAspect {<!-- -->

 @Pointcut("execution(* com.zeng.spring.controller.AopTestAspectController.*(..))")
  public void logJoinPoint() {<!-- -->

  }

  @Before("logJoinPoint()")
  public void beforeAdvice(JoinPoint joinPoint) {<!-- -->
    String format = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now());

    System.out.println("beforeAdvice executed>>>>>>>>>>>>>>>>>>>>>>>>" + format);
  }
}

We can define a controller test to verify it.

package com.zeng.spring.controller;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.text.DateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;

@RestController
@RequestMapping("/aop")
public class AopTestAspectController {<!-- -->

  @GetMapping("/before")
  public void before() {<!-- -->
    Calendar instance = Calendar.getInstance();

    String format = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now());
    System.out.println("AopTestAspectController was called successfully" + format);
  }
}

You can see the result. The method in aop is executed before executing the target method.

In this method of beforeAdvice, we have a parameter JoinPoint. This parameter can get the information of our method call, such as getting the input parameters of our method. We redefine a controller interface and define a parameter that needs to be input.

 @GetMapping("/before/{param}")
  public void before(@PathVariable("param") String param) {<!-- -->
    System.out.println("The before method with input parameters in AopTestAspectController is executed" + "Parameter: " + param);
  }

Then get the method parameters through JoinPoint.getArgs, we can see the results: and get the parameters in the method

 @Before("logJoinPoint()")
  public void beforeAdvice(JoinPoint joinPoint) {<!-- -->
    Object[] args = joinPoint.getArgs();
    for (Object arg : args) {<!-- -->
      System.out.println("The parameters received by beforeAdvice are:" + arg);
    }
    String format = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now());

    System.out.println("beforeAdvice executed>>>>>>>>>>>>>>>>>>>>>>>>" + format);
  }

2. Use of @After

We only need to replace the @Before annotation with the @After annotation.

 @After("logJoinPoint()")
  public void afterAdvice(JoinPoint joinPoint) {<!-- -->
    String format = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now());
    System.out.println("afterAdvice executed>>>>>>>>>>>>>>>>>>>>>>" + format);
  }

It can be found that the result is completed after the method ends.

3. Use of @AfterReturning

This is executed after the target method (entry point) returns the result. We can obtain the return value information through this method. Look at the result and the return value of the method has been successfully obtained.

  @AfterReturning(value = "logJoinPoint()",returning = "res")
  public void afterReturning(Object res) {
    System.out.println("afterReturning method is executed to obtain the replacement value and the result is>>>>>>>>>>>>>>>>" + res.toString());
  }

4. Use of @Around

Surround advice is relatively powerful. It is executed around the entire method. We can either perform operations before the target method is executed or enhance it after the target method is executed. Looking at the results below, you can see that it is executed before and after the target method is executed.

 @Around("logJoinPoint()")
  public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {<!-- -->

    System.out.println("around method started execution>>>>>>>>>" + DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now ()));
    //execution method
    proceedingJoinPoint.proceed();
    System.out.println("around method execution ends" + DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now()));
  }

Similarly, it can also obtain the input parameters and return values of the method. It is worth noting that the surrounding notification uses the ProceedingJoinPoint parameter. Next let’s see how he gets the parameters.


  @Around("logJoinPoint()")
  public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {<!-- -->
    System.out.println("around method started execution>>>>>>>>>" + DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now ()));
    Object[] args = proceedingJoinPoint.getArgs();
    for (Object arg : args) {<!-- -->
      System.out.println("The input parameters of the around notification method are" + arg);
    }
    //execution method
    Object proceed = proceedingJoinPoint.proceed();
    System.out.println("around notification got the returned parameters: " + proceed.toString());
    System.out.println("around method execution ends" + DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").format(LocalDateTime.now()));
  }

Looking at the results, we have obtained the input parameters and return value of the method.

5. Use of @AfterThrowing

This will only be triggered after an exception occurs in the method. Let’s try simulating a divide-by-zero exception. Triggered when using exception

 @AfterThrowing(value = "logJoinPoint()", throwing = "ex")
  public void afterThrowing(Exception ex) {<!-- -->
    System.out.println("afterThrowing received an exception, the exception message is:" + ex.getMessage());
  }

Looking at the results, we found that we have received specific error messages.