“This is the third day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.

The introduction

When you are developing a background management system, you suddenly encounter the need to record user operation logs, what method will you take? There are several ways to solve this problem, such as: the logic of writing the print log everywhere (which is obviously not wise), using the proxy pattern (AOP’s underlying principle)…

This article takes you through a more ideal solution to this requirement — logging using the AOP library provided by AspectJ and logging on the controller with Java’s Annotation Annotation.

AOP is introduced

Before I begin, I’ll briefly introduce some of the concepts of AOP. If you are completely new to these concepts, it is recommended that you go online and read on.

AOP, Aspect Oriented Programming, that is, Aspect Oriented Programming.

  • Aspect: An Aspect, similar to a class declaration in Java, is a modular encapsulation of concerns in a system that spans multiple classes

  • Joinpoint: a “point” in program execution, such as method execution or exception handling. In Spring AOP, a join point always represents the execution of a method.

  • Pointcut: a Pointcut, which is a set of join points

  • Advice: defines the specific logic that will be weaved into Joinpoint. Annotations such as @before, @after, and @around are used to distinguish between executing code Before and After JointPoint

  • Weaving: Weaving refers to the process of connecting Advice to the Joinpoint specified by Pointcut

  • Interceptor: an Interceptor, a way to implement Advice;

  • Target: The Target object that is woven into Advice that meets the criteria specified by the pointcut.

You might be a little confused by the above terminology, but that’s ok, we’ll get into formal Coding, step by step, and then come back to this section and look at these concepts.

Coding — Start implementing the logging module

Introduction of depend on

In the case of SpringBoot, first we import the required AOP dependencies from Maven’s central repository. Add the following code to your pom.xml:

		<! -- AOP dependencies -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
Copy the code

Custom annotation

Annotations are defined so that where logs need to be printed later, only @log (” module name “,” business type “) is needed.


We use IDEA right click ->New->Java Class->Annotation, file name log.java. The code snippet is as follows:


@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {

    /** * Module name */
    public String moduleName(a) default "";

    /** * Service type */
    public int businessType(a) default BusinessType.OTHER; 
    // The service type can be defined by yourself. For complete code, please refer to Github
}

Copy the code

In the Log annotation, we define two parameters that the annotation can receive: module name and business type. And specify the Target Target as a METHOD METHOD, which is the METHOD annotation.

@Retention(retentionPolicy.runtime) indicates that the annotation is not only saved in the class file, but remains after the JVM loads the class file.

Write the aspect class

The core code for logging facets. The code in this class dynamically injects into the cross section of the annotation method and implements logging.


@Component
@Aspect
public class LogAspect {

    @Pointcut("@annotation(com.yeliheng.blogcommon.annotation.Log)")
    public void pointcut(a) {}

    @AfterReturning(pointcut = "pointcut()", returning = "jsonResponse")
    public void doReturningLogRecord(JoinPoint joinPoint, Object jsonResponse) {
        recordLog(joinPoint,null,jsonResponse);
    }

    @AfterThrowing(value = "pointcut()", throwing = "e")
    public void doThrowingLogRecord(JoinPoint joinPoint, ApiException e) {
        recordLog(joinPoint,e,null);
    }

    private void recordLog(JoinPoint joinPoint, ApiException e, Object jsonResponse) {
        Log log = getAnnotation(joinPoint);
        if(log == null) {
            return;
        }
        //TODO:The specific logic of logging is as follows: Obtaining various information, writing to the database...

    }

    /** * Get annotations by reflection */
    private Log getAnnotation(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if(method ! =null) {
            return method.getAnnotation(Log.class);
        }
        return null; }}Copy the code

To implement the Aspect class, we need to add two annotations in front of the class: @Component and @aspect, which are managed by the Spring IoC container. When Spring’s IoC container reads Aspect annotations, it automatically looks for beans with @aspect and then injects AOP into specific beans based on interceptor annotations like @AfterRETURNING and @AfterThrowing.

Interceptor notes:

  • AfterReturning: Methods will be intercepted by this interceptor when they return normally.
  • AfterThrowing: When an exception is thrown during a method run, it is intercepted by this interceptor.

In both doReturningLogRecord and doThrowingLogRecord, we pass in a joinPoint, and in the recordLog method we get the custom annotation that we defined in joinPoint — @log, The method name and class name can be retrieved from the join point through reflection.

Now that we know what the interceptor method does, we can write specific logging logic in the recordLog method. For example, record the operation time, operator, location, etc., and write these information into the database.

Begin to use

After writing the section class, your custom annotation log should be working and logging to the user. But how do we use it?


    ModuleName = "new user ",businessType = businessType.INSERT)
    @PostMapping
    public CommonResponse<Object> add(@Validated @RequestBody User user) {
        userService.insertUser(user);
        return CommonResponse.success();
    }

Copy the code

The approach is very simple: just add @log (” module name “,” business type “) to the corresponding controller method, and the JVM will use its dynamic proxy capabilities to compile the corresponding code into bytecode and weave it into the corresponding cross section. This is AOP’s best practice for faceted programming.

conclusion

This article describes how to use Spring AOP to log user operations. If you want to learn more about how Spring AOP works, please visit the Spring official website, refer to the Spring manual or use the source code for in-depth analysis.

See Github for the full code