本篇教程没有实际生产意义,进作为学习、复习、研究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();
}
}
}
}