AOP custom aspects to realize automatic filling processing of public fields

1.1 Problem Analysis

Each table has fields such as creation time, creator, modification time, and modification person.

Manual assignment of values during operation is relatively redundant and cumbersome. Can these public fields be processed uniformly somewhere to simplify development?

The answer is yes, we use AOP aspect programming to implement functional enhancements to complete the automatic filling function of public fields.

1.2 Implementation ideas

When implementing automatic filling of public fields, that is, assigning specified values to specified fields when inserting or updating, the advantage of using it is that these fields can be processed uniformly, avoiding duplication of code. In the above problem analysis, we mentioned that there are four public fields that need to be assigned values during new addition/update. The specific situation is as follows:

Serial number Field name Meaning Data type
1 create_time Creation time datetime
2 create_user Creator id bigint
3 update_time Modification time datetime
4 update_user Modify person id bigint
Serial number Field name Meaning Data type Operation type
1 create_time Creation time datetime insert
2 create_user Creator id bigint insert
3 update_time Modification time datetime insert, update
4 update_user Modifier id bigint insert, update

Implementation steps:

1). Custom annotation AutoFill, used to identify methods that require automatic filling of public fields

2). Customize the aspect class AutoFillAspect to uniformly intercept methods with AutoFill annotations and assign values to public fields through reflection.

3). Add AutoFill annotation to the Mapper method

1.3 Code Development

Public dependencies

Create the AutoFillConstant class under the common constant package

package com.sky.constant;

/**
 * Public fields automatically fill in relevant constants
 */
public class AutoFillConstant {
    /**
     *Method name in entity class
     */
    public static final String SET_CREATE_TIME = "setCreateTime";
    public static final String SET_UPDATE_TIME = "setUpdateTime";
    public static final String SET_CREATE_USER = "setCreateUser";
    public static final String SET_UPDATE_USER = "setUpdateUser";
}

Create the OperationType class under the common enumeration package

package com.sky.enumeration;

/**
 * Database operation type
 */
public enum OperationType {

    /**
     * Update operation
     */
    UPDATE,

    /**
     * Insert operation
     */
    INSERT

}
1.3.1 Step 1

Custom annotation AutoFill

Enter the server module, create annotation package, and create AutoFill annotation

package com.sky.annotation;

import com.sky.enumeration.OperationType;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Custom annotations used to identify a method that requires automatic filling of function fields
 */
@Target(ElementType.METHOD)//Specify that this annotation can only be added to the method
@Retention(RetentionPolicy.RUNTIME)//JVM retains this annotation at runtime and allows access through reflection
public @interface AutoFill {
    //Specify the database operation mode through enumeration: update insert
    OperationType value();
}
1.3.2 Step 2

Custom aspect AutoFillAspect

In the server module, create the aspect package and AutoFillAspect class

package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * Customize aspects to implement automatic filling processing logic of public fields
 */
@Aspect//The statement is an aspect
@Component//bean is handed over to spring container management
@Slf4j
public class AutoFillAspect {

    /**
     * Define entry point
     */
    //point-cut expression
    @Pointcut("execution(* com.sky.mapper.*.*(..)) & amp; & amp; @annotation(com.sky.annotation.AutoFill)")//Intercept the method below mapper and add AutoFill annotation
    public void autoFillPointCut(){}

    /**
     * Define pre-notifications and assign values to public fields in the notifications
     */
    //Pre-notification, before performing the operation
    @Before("autoFillPointCut()")//When matching the upper point cut expression, execute the notification method
    public void autoFill(JoinPoint joinPoint){//Join point, which method was intercepted, and the intercepted method parameter value and type
        log.info("Start automatic filling of public fields...");

        /**
         *Notification content
         */
        //1. Obtain the database operation type on the currently intercepted method
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//Method signature object, Signature is the interface converted to MethodSignature sub-interface
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//Get the annotation object on the method
        OperationType operationType = autoFill.value();//Get the database operation type written on the mapper method

        //2. Get the parameters of the currently intercepted method, which is the entity object
        Object[] args = joinPoint.getArgs();//Get the parameters of the connection point. There are multiple parameters. It is agreed that the first parameter is the entity.
        if(args == null || args.length == 0){
            return;
        }

        Object entity = args[0];//In the database operation method, it is agreed that the entity class must be the first parameter to obtain the entity

        //3. Prepare data for assignment
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();

        //4. According to the current different operation types, assign values to the corresponding attributes through reflection.
        if(operationType == OperationType.INSERT){
            //Assign values to 4 public fields
            try {
                //Get set method
                // Standardize to prevent writing errors and write method names as constant classes
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //Assign values to object properties through reflection
                setCreateTime.invoke(entity,now);
                setCreateUser.invoke(entity,currentId);
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if(operationType == OperationType.UPDATE){
            //Assign values to 2 public fields
            try {
                //Get set method
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                //Assign values to object properties through reflection
                setUpdateTime.invoke(entity,now);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
1.3.3 Step 3

Add AutoFill annotation to the method of Mapper interface

Example:

@AutoFill(value = OperationType.INSERT)
void insert(Category category);

@AutoFill(value = OperationType.UPDATE)
void update(Category category);