AOP – 给返回值追加注释字段

2023-06-07 16:47:08 浏览数 (2)

本篇教程没有实际生产意义,进作为学习、复习、研究AOP使用 本教程是在还原京东架构字典注解的方式之一的初级DEMO,生产使用需要额外拓展使用。

AOP与拦截器实际操作的时候,拦截器的HttpServletRequest对象无法修改Request的内容。

我们使用AOP的时候,尽管方法入参没有相关对象。我们也可以获取request,response对象,一般是用如下代码:

代码语言:javascript复制
                ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = requestAttributes.getRequest();
                HttpServletResponse response = requestAttributes.getResponse();

开始正文

创建注解 AppendFieldDesc

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

/**
 * 此注解将会给XX字段,追加一个XXDesc字段用
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD}) // 仅仅作用在字段上
public @interface AppendFieldDesc {

}

创建注解 AppendReturn

代码语言: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.METHOD}) // 仅仅作用在字段上
public @interface AppendReturn {

}

创建切面 AppendAspect

代码语言:javascript复制
package com.zanglikun.springdataredisdemo.aop.appendDescAop.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.zanglikun.springdataredisdemo.aop.appendDescAop.annotation.AppendFieldDesc;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;

/**
 * 本配置是用于处理@AppendXXDesc注解的时候,给字段追加Desc注释使用
 * 注意依赖Jackson 处理序列化的值,我没研究过Jackson,不过可以考虑更换为其他处理序列化的方法
 * 本织入点在返回值处理
 */
@Aspect
@Component
public class AppendAspect {
    @Pointcut("@annotation(com.zanglikun.springdataredisdemo.aop.appendDescAop.annotation.AppendReturn)")
    public void AppendAspect() {
    }

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * Advice返回后执行,也就是"拦截器出"的时候,也就是进入"出过滤器"前的时候执行,白话粗略理解就是:返回前端前最后一步处理。
     *
     * @param joinPoint
     * @param returnValue 与返回内容保持一致
     * @throws JsonProcessingException
     */
    // 注意环绕的切点表达式必须是&&相同,不然会出现重复调用IO的问题
    //@AfterReturning(value = "AppendAspect()", returning = "returnValue")
    @AfterReturning(value = "execution(public * com.zanglikun.springdataredisdemo.controller.*.*(..)) && AppendAspect()", returning = "returnValue")
    public void appendXXDesc(JoinPoint joinPoint, Object returnValue) throws JsonProcessingException {
        if (returnValue != null) {
            String json = objectMapper.writeValueAsString(returnValue); // 将返回值序列化后的Json
            ObjectNode objectNode = (ObjectNode) objectMapper.readTree(json); // 读取每个节点的内容,也就是遍历每一个{}的元素
            for (Field field : returnValue.getClass().getDeclaredFields()) { // 获取这个元素的字节码文件,解析其所有字段
                if (field.isAnnotationPresent(AppendFieldDesc.class)) { // 判断字段是否有被标记注解
                    String fieldName = field.getName(); // 获取字段名称
                    JsonNode fieldValue = objectNode.get(fieldName); // 读取字段值
                    if (fieldValue != null) { // 如果字段值不是空
                        objectNode.put(fieldName   "Desc", fieldValue.asText()   "XXDesc"); // 我们往序列化的Tree写入一个XXDesc的字段,一会IO写入即可
                    }
                }
            }
            // 获取当前的线程响应对象,并设置响应头
            ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
            OutputStream writer = null;
            try {
                // 注意,这里响应只能使用一种流 getResponse().getOutputStream() 或者 getResponse().getWriter()
                ServletOutputStream outputStream = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse().getOutputStream();
                writer = outputStream;
                // 获取当前线程的响应流对象。并写入我们的最新的序列化字段,记得关流哦!
                writer.write(objectNode.toString().getBytes());
                writer.flush();
                writer.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

0 人点赞