Recording user actions to change field implementation (written in the New Year's Eve, cc)

Foreword

Before technology group where there is a big brother made a demand: record the user's operation. A seemingly simple and complex needs, I started brainstorming:

  1. Comparative recording distal
  2. Backend comparison

  Which ultimately comparison process. Then the group proposed the idea that a more constructive Gangster, this demand after the wildcard, rather than every before copying it again to logic, that is, duplication of work. He believes that there is no association with the business, can be abstract, for later use.

  This is a very meaningful idea, worthy of our routine work to learn. Thereby enhancing our daily development. Like before seeing a Gangster Summary: 28 principle, 20% of energy on crud, after 80% of the time to deal with the team, the whole thing influential technology, this is the more valuable.

  The following is a brother towards the idea of ​​fat reference, use custom annotations, add comments on the need to compare the field, when a query is written redis. When editing, the key pass over the cache, the cache comparison with the previous, after comparing the difference store database, delete cache ~

show y the code like the s***

  I wrote the code is not ye drops, in fact, cc. Look like ~

Custom annotation

Notes on the method, because I want to use aop.

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)  // 保留到运行时,可通过注解获取
@Documented
public @interface MyMethod {

    Class toClass() ;

}

Notes to the field

@Target(ElementType.FIELD)  //  注解用于字段上
@Retention(RetentionPolicy.RUNTIME)  // 保留到运行时,可通过注解获取
public @interface MyField {

}

Logic section

import com.alibaba.fastjson.JSON;
import com.example.demo.annotation.MyField;
import com.example.demo.annotation.MyMethod;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
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.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author M
 */
@Aspect
@Component
public class TestAspect {

    @Autowired
    private StringRedisTemplate template;

    private static ThreadLocal<String> cacheKey = new ThreadLocal<>();

    @Pointcut("@annotation(com.example.demo.annotation.MyMethod)")

    public void annotationPoinCut() {

    }

    @Before(value = "annotationPoinCut()")
    public void beforeTest(JoinPoint point) throws IllegalAccessException {
        //获取方法签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        //获取切入方法的对象
        Method method = signature.getMethod();
        //获取方法上的Aop注解
        MyMethod myMethod = method.getAnnotation(MyMethod.class);
        String args = JSON.toJSONString(point.getArgs());
        if (args.length() > 1) {
            Object dto = JSON.parseObject(args.substring(1, args.length() - 1), myMethod.toClass());
            xx(dto, myMethod.toClass());
        }


    }

    private void xx(Object dto, Class class1) throws IllegalAccessException {
        // 获取所有字段
        Object lastDto = null;
        for (Field f : class1.getDeclaredFields()) {
            f.setAccessible(true);
            //System.out.println("f:" + f.getName());
            // 判断这个字段是否有MyField注解
            if ("key".equals(f.getName()) && !StringUtils.isEmpty(f.get(dto))) {
                lastDto = JSON.parseObject(template.opsForValue().get(f.get(dto)), class1);
                cacheKey.set(String.valueOf(f.get(dto)));
            }
            System.out.println(lastDto);
            if (f.isAnnotationPresent(MyField.class) && lastDto != null) {
                Object value = f.get(dto);
                Object value1 = f.get(lastDto);
                if (value != value1) {
                    System.out.println("存在不同,之前的值为:" + value1 + ",后面的值:" + value);
                }
            }
        }
    }

    @After(value = "annotationPoinCut()")
    public void afterTest(JoinPoint point) {
        System.out.println(cacheKey.get());
        template.delete(cacheKey.get());
        cacheKey.remove();
    }

}

explain

  MyMethod first cut to the method, which is behind toClass used to instantiate an object, and then obtain the request parameters, the value of the object back to the inside. The traversal attribute class Field, then get on the related annotations @MyField attribute value of the attribute acquired, for comparison. Use ThreadLocal to maintain cache data before redis period, and then delete the cache after processing.

Traversal property

public static void main(String[] args) {
        // 获取类模板
        Class c = DajituiDTO.class;
        // 获取所有字段
        for (Field f : c.getDeclaredFields()) {
            // 判断这个字段是否有MyField注解
            if (f.isAnnotationPresent(MyField.class)) {
                MyField annotation = f.getAnnotation(MyField.class);
                System.out.println("字段:[" + f.getName() + "]");
            }
        }
    }

dto

@AllArgsConstructor
@Data
@NoArgsConstructor
public class DajituiDTO {

    private String key;

    @MyField
    private String name;

    private Integer age;

    public static void main(String[] args) {
        // 获取类模板
        Class c = DajituiDTO.class;
        // 获取所有字段
        for (Field f : c.getDeclaredFields()) {
            // 判断这个字段是否有MyField注解
            if (f.isAnnotationPresent(MyField.class)) {
                MyField annotation = f.getAnnotation(MyField.class);
                System.out.println("字段:[" + f.getName() + "]");
            }
        }
    }
}

controller

import com.alibaba.fastjson.JSON;
import com.example.demo.annotation.MyMethod;
import com.example.demo.dto.DajituiDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * @author M
 */
@RestController
public class TestController {

    @Autowired
    private StringRedisTemplate template;

    @GetMapping("search")
    public String getSomeThing() {
        DajituiDTO dto = new DajituiDTO("大鸡腿", null, 18);
        String key = getTheKey("search");
        dto.setKey(key);
        template.opsForValue().set(key, JSON.toJSONString(dto), 10, TimeUnit.MINUTES);
        return dto.toString();
    }

    @GetMapping("edit")
    //@MyMethod(toClass = DajituiDTO.class)
    public String editSomeThing(DajituiDTO dto) {
        //修改并显示有注解字段的改变情况
        return dto.toString();
    }

    @PostMapping("edit1")
    @MyMethod(toClass = DajituiDTO.class)
    public String editSomeThingPost(@RequestBody DajituiDTO dto) {
        //修改并显示有注解字段的改变情况
        return dto.toString();
    }

    private String getTheKey(String requestUrl) {
        return System.currentTimeMillis() + requestUrl + ThreadLocalRandom.current().nextInt();
    }


}

Request link

http: // localhost: 8080 / search save information
HTTP: // localhost:? 8080 / Edit key = 1579857090663search397251701 & name = 123
key words of this key requires a link back to the first pass as a parameter

Or post request http: // localhost: 8080 / edit1

{
    "key": "1579858080821search1554559961",
    "name": "123"
}

Export

DajituiDTO (Key = 1579858080821search1554559961, name = null, Age = 18 is)
DajituiDTO (Key = 1579858080821search1554559961, name = null, Age = 18 is)
there are different, the previous value: null, the latter value: 123
DajituiDTO (Key = 1579858080821search1554559961, name null =, Age = 18 is)
1579858080821search1554559961

No value can be seen before the name, modify back to 123.

Improving local

Cache key generation

private String getTheKey(String requestUrl) {
        return System.currentTimeMillis() + requestUrl + ThreadLocalRandom.current().nextInt();
    }

  Why here using timestamps? Because at that time the big guys when discussing how to compare concurrent operations, where a bit like a version of something. For example, a iphone generation, we have to compare it to compare the generation, meaning iphone if we compare to other generations, a contrast not so long.
  Secondly, what may really be time to consider what are just right, the user can add a name to what's on top.

Logic section

  Some require a judgment on which logically empty, and this type of conversion, needs to be improved -

github

The large chicken github

At last

  It is this chicken in the New Year's Eve to write a blog, and sometimes inadequate, you big brother a lot of guidance. Secondly, whenever and wherever, and whenever we must constantly strive to learn, the EU force to ~

Published 213 original articles · won praise 31 · views 190 000 +

Guess you like

Origin blog.csdn.net/weixin_38336658/article/details/104081139