Three things about aspect-oriented programming——explain the actual use of AOP through double deletion of redis cache

What is AOP?

AOP is what we often call aspect-oriented programming. It is a technology that achieves unified maintenance of program functions through pre-compilation and runtime dynamic proxy! Spring AOP is a standard and easy-to-use AOP framework provided by Spring, which is realized through the dynamic proxy technology provided by Spring. During operation, Spring dynamically generates proxy objects through dynamic proxy technology. When the proxy object method is executed, it intervenes to enhance the function, and calls the method of the target object to complete the function enhancement.

Why use AOP?

1. Functional enhancement . During the running of the program, the function of the method is enhanced without modifying the source code;
2. It is easy to maintain , and the redundant and must-executable code is put into AOP for execution, reducing repeated code and improving development efficiency. And it is easy to maintain;
3. Reduce the coupling and isolate each part of the business logic, so that the coupling degree between the parts of the business logic is reduced and the reusability of the program is improved;

How to use AOP?

The method of using AOP is: set entry point + implementation of aspect logic;
entry point: create entry point annotation class, and add corresponding entry point annotation before the method that needs to perform aspect operation;
aspect logic implementation: in the aspect implementation class according to the business situation The business logic is implemented in the corresponding notification method. The aspect notification methods mainly include pre-notification, post-notification, exception notification, surround notification, etc. Generally, surround notification is used to realize the business logic!
1. Introduce dependencies

<!-- aop -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. Create annotation
Add annotation class, you can design annotation filling parameters according to business situation! Two fields are designed here, one is the redis key-value prefix, and the other is the key-value pair composition field! The key value of the redis cache is formed by prefixing the key-value pair with the value of the key-value composition field, so as to operate the cache!

/**
 * redis数据双删,实现数据一致性
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoDoubleDelRedisData {
    
    
    /******************redis键值为前缀加键值组成部分*******************/
    /**
     * redis 键值前缀
     */
    String[] prefixs();

    /**
     * redis 键值组成字段
     */
    String[] keyFields() default {
    
    };
}

3. Complete the aspect logic
Create an aspect implementation class for business implementation after entering the aspect! Use the @pointcut annotation to locate the entry point, and implement the functions that need to be implemented after entering the aspect in @Around (generally, surround notifications are used, or you can use notification methods such as pre-notification @Before or post-notification @After to implement the logic of the aspect according to the actual situation. )! Here, the cache operation is performed before the entry point, and then a cache deletion is performed after the method is executed, so as to achieve cache data consistency.

@Aspect
@Component
public class AutoDoubleDelRedisDataAspect {
    
    
    //切入点
    @Pointcut("@annotation(com.example.redisdemo.aop.annotation.AutoDoubleDelRedisData)")
    public void doPointcut() {
    
    
    }

    /**
     * 环绕通知
     */
    @Around("doPointcut()")
    public Object doAroundAdvice(ProceedingJoinPoint point) throws Throwable {
    
    
        System.out.println("----------- 环绕通知 -----------");
        System.out.println("切入点目标方法名:" + point.getSignature().getName());
        //获取入参
        Object[] objs = point.getArgs();
        Object paramObject = null;
        if (objs.length > 0) {
    
    
            paramObject = objs[0];
        }
        //通过自定义注释获取自定义注解的方法对象
        MethodSignature methodSignature = (MethodSignature)point.getSignature();
        Method targetMethod = methodSignature.getMethod();//方法对象
        AutoDoubleDelRedisData annotation = targetMethod.getAnnotation(AutoDoubleDelRedisData.class);
        String[]  prefixs    = annotation.prefixs();
        String[] keyFields = annotation.keyFields();

        System.out.println("方法执行前:" + LocalDateTime.now());
        delRedisCache(paramObject,prefixs,keyFields);
        Object proceed = point.proceed();
        System.out.println("方法执行后:" + LocalDateTime.now());
        //可以设置延迟删除
        delRedisCache(paramObject,prefixs,keyFields);
        return proceed;
    }

    /**
     * 删除缓存信息
     * zlx
     * 16:29 2022/6/18
     * @param paramObject   入参参数
     * @param prefixs        键值前缀
     * @param keyFields     键值对饮id
     * @return void
     **/
    private void delRedisCache(Object paramObject,String[] prefixs,String[] keyFields) {
    
    
        for (int i = 0; i < prefixs.length; i++) {
    
    
            String redisKey = prefixs[i];
            String keyField = keyFields[i];
            if (paramObject.getClass() == String.class) {
    
    
                if (keyField != null && StrUtil.equals(keyField,"self")) {
    
    
                    redisKey = redisKey + paramObject;
                } else if (keyField != null && keyField.contains(";")) {
    
    
                    redisKey = buildRedisKey(redisKey,keyField,paramObject);
                }
            } else {
    
    
                if (StrUtil.isNotBlank(keyField)) {
    
    
                    redisKey = buildRedisKey(redisKey,keyField,paramObject);
                }
            }
            RedisUtils.scanDelKeys(redisKey);
        }

    }

    private String buildRedisKey(String redisKey, String keyField, Object paramObject) {
    
    
        String[] keys = keyField.split(";");
        for (int j = 0; j < keys.length; j++) {
    
    
            String key = keys[j];
            redisKey = redisKey + ReflectUtil.getFieldValue(paramObject, key);
            if (j == keys.length - 1) {
    
    
                redisKey = redisKey + "*";
            } else {
    
    
                redisKey = redisKey + ":";
            }
        }
        return redisKey;
    }

4. Add section annotations according to actual business needs, such as modifying and deleting information, just
add @AutoDoubleDelRedisData(prefixs = “RECORD:”, keyFields = “recordId”) annotation before the method that needs to be sectioned.

    /**
     * 根据记录id删除记录
     * zlx
     * 10:09 2022/6/18
     * @param recordId 记录id
     * @return boolean
     **/
    @Override
    @AutoDoubleDelRedisData(prefixs = "RECORD:",keyFields = "self")
    public boolean delRecord(String recordId) {
    
    
        return this.removeById(recordId);
    }

    /**
     * 修改记录内容
     * zlx
     * 10:06 2022/6/18
     * @param record    修改记录内容
     * @return com.example.redisdemo.entity.Record
     **/
    @Override
    @AutoDoubleDelRedisData(prefixs = "RECORD:",keyFields = "recordId")
    public Record putRecord(Record record) {
    
    
        record.setUpdateTime(LocalDateTime.now());
        if (this.updateById(record)) {
    
    
            return record;
        }
        return null;
    }

In this way, we have realized the operation of automatically deleting the redis cache through the aop aspect! Can be tested via http files! When we modify or delete a record, it will enter the aspect implementation class and automatically delete the corresponding cache information!
insert image description here

demo code address: https://gitee.com/zlx041192/redis-double-deletion-demo

Guess you like

Origin blog.csdn.net/xianren95/article/details/126626585