字典值替换注解 – AOP最佳实践

2023-06-07 16:46:11 浏览数 (2)

定义注解 DictRetun

代码语言:javascript复制
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 此注解将会给方法加上标记,以触发读取方法参数是否有AppendFileldDesc注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER}) // 仅仅作用在入参上
public @interface DictRetun {

}

定义注解 DictFild

代码语言:javascript复制
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DictFild {
}

定义织入

代码语言:javascript复制
import com.zanglikun.springdataredisdemo.aop.dictAop.annotation.DictFild;
import com.zanglikun.springdataredisdemo.aop.dictAop.annotation.DictRetun;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;

@Aspect // 声明切面类
@Component // 注册为Spring组件,不然切面类不生效,这里我是先关闭了,你使用请务必开启
@Slf4j
public class DictAdvice {


    /**
     * Pointcut 定义切入点,一般为方法级别。通过切点表达式体现。
     * A原表达式:execution(public * com.zanglikun.springdataredisdemo.controller..*.*(..))
     * A表达式是切入 com.zanglikun.springdataredisdemo.controller 下面所有的Public方法
     * <p>
     * 一般还有匹配注解的如:@annotation(com.zanglikun.springdataredisdemo.aop.fileLimitAop.FileLimit)
     * 就是切入被FileLimit的注解
     */
    @Pointcut("@annotation(com.zanglikun.springdataredisdemo.aop.dictAop.annotation.DictRetun)")
    public void dictPointcut() {
    }

    // 定一个变量,以便于全局获得模拟的DB返回的字典信息
    HashMap<String, HashMap<String, Object>> returnMap = new HashMap<>();

    // 自己主动去缓存Cache Code=Name oldValue NewValue
    HashMap<String, HashMap<String, Object>> getDictionary() {
        HashMap<String, Object> dbReturn = new HashMap<>();
        dbReturn.put("1", "张三");
        dbReturn.put("2", "李四");
        dbReturn.put("3", "刘能");
        returnMap.put("name", dbReturn);
        return returnMap;
    }

    /**
     * 环绕通知
     * 理解点1:joinPoint.proceed()作用是:执行被拦截的方法。
     * 为什么叫环绕通知呢?我们一般在joinPoint.proceed()。上下位置放置我们要执行的代码,可以实现在方法前、后执行代码
     *
     * @param joinPoint 切点 注意Around的joinPoint 可以与其他的实现JoinPoint不一样
     * @return 返回的内容未来要返回给前端使用哦
     * @throws Throwable
     * @Around("onePointCut()") 这种定义切入点方式和直接在@Around里面声明具体得切入点表达式一样
     */
    @Around("dictPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object proceed = joinPoint.proceed();//调用被切入方法,并获取其返回值
        Object joinPointInfo = getJoinPointInfo(joinPoint, proceed);
        return joinPointInfo;
    }

    /**
     * 替换字典的操作
     * @param joinPoint 切点信息
     * @param returnRes 方法返回的内容
     * @return 处理returnRes后再返回
     */
    public Object getJoinPointInfo(JoinPoint joinPoint, Object returnRes) {
        // 这串代码的含义是获取HttpServletRequest、HttpServletResponse获取的是本线程的哦
        {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();
            HttpServletResponse response = requestAttributes.getResponse();
        }

        Object[] args = joinPoint.getArgs(); // 获取方法的所有入参值,但是无法准确获取入参类型,但是要去修改属性值,必须得获取对象
        for (Object arg : args) {
            System.out.println(arg);
        }

        // 获取AOP签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod(); // 获取被调用的方法
        Class<?> returnType = method.getReturnType(); // 获取返回值的类型
        Parameter[] parameters = method.getParameters(); // 获取方法的入参
        for (Parameter parameter : parameters) {
            parameter.getName(); // 获取参数名称例如:User user 获取的是user
            Annotation[] classAnnotations = parameter.getAnnotations(); // 获取该入参的所有注解
            Class<?> type = parameter.getType(); // 获取入参的字节码文件以便于快速获得变量的属性
            if (parameter.isAnnotationPresent(DictRetun.class)) { // 如果该变量被DictRetun修饰了,才会执行
                Field[] declaredFields = type.getDeclaredFields(); // 获取该变量的所有属性列表
                for (Field declaredField : declaredFields) {
                    if (declaredField.isAnnotationPresent(DictFild.class)) { // 如果变量被DictFild修饰后,才执行
                        // 开始替换为字典值
                        try {
                            declaredField.setAccessible(true);
                            Object o = declaredField.get(returnRes);
                            if (o instanceof String) {
                                getDictionary(); // 初始化数据
                                HashMap<String, Object> stringObjectHashMap = returnMap.get(declaredField.getName());
                                declaredField.set(returnRes, stringObjectHashMap.get(o));
                            }
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        } finally {
                            declaredField.setAccessible(false);
                        }
                    }
                }
            }
        }
        return returnRes;
    }
}

定义实体 User

代码语言:javascript复制
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
    @DictFild
    private String name;
}

测试代码

代码语言:javascript复制
    @PostMapping("/testArgsAnno")
    @ResponseBody
    public User testArgsAnno(@DictRetun @RequestBody User user) {
        return user;
    }

测试结果

结果1

代码语言:javascript复制
【入参】:
{
    "name": "1"
}

【出参】:
{
    "name": "张三"
}

结果2

代码语言:javascript复制
【入参】:
{
    "name": "2"
}

【出参】:
{
    "name": "李四"
}

0 人点赞