有这么一个需求:记录controller的请求参数和响应结果到日志中。
解决思路:写一个自定义注解,在需要记录的controller的方法上,加上该注解,通过注解来记录相关信息。
解决方式:使用AOP来解决,通过返回通知来获取返回结果信息。
解决步骤:
1、在pom.xml中引入AOP的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
2、定义枚举类,针对方法参数制定枚举类型
public enum ParamTypeEnum {
PARAM_BEAN("bean","bean"),//bean类型
PARAM_KAK_JSON("k1-v1,k2-v2,...,json","kak_json"),//k-v键值对和json串混合类型
PARAM_KAK_MAP("k1-v1,k2-v2,...,map","kak_map"),//k-v键值对和map混合类型
PARAM_KAK("k1-v1,k2-v2,...","kak"),//k-v键值对类型
PARAM_MAP("map","map"),//map类型
PARAM_JSON("json","json");//json类型
private String name;
private String code;
public String getName() {
return this.name;
}
public String getCode() {
return this.code;
}
private ParamTypeEnum(String name, String code){
this.name = name;
this.code = code;
}
}
3、定义自定义注解
//以下3个元注解的作用,自行百度即可
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ManagerLog {
/**
* 参数类型,一共6种
* 1、json
* 2、map
* 3、K-V键值对
* 4、K-V键值对 与 json 混合
* 5、K-V键值对 与 map 混合
* 6、bean
* @return
*/
ParamTypeEnum paramType();
/**
* 参数为混合参数时使用,标记map/json所在的参数位置,从0开始计数
* @return
*/
int mulIndex() default -1;
/**
* json是否为嵌套json,仅支持2级
* @return
*/
boolean mulJson() default false;
/**
* 举例:{"data":{"a":1,"b":2}}
* 嵌套json的key
* @return
*/
String childJsonName() default "data";
}
4、AOP实现
@Aspect
@Component
public class ManagerLogAspect {
@Resource
private KafkaManagerLogSenderUtil kafkaManagerLogSenderUtil;//将日志信息发送到kafka
@Pointcut("@annotation(com.chineseall.admin.annotation.ManagerLog)")
public void annotationPointCut() {
}
@AfterReturning(returning = "result",value = "annotationPointCut()&&@annotation(managerLog)")
public Object afterReturn(JoinPoint point, Object result, ManagerLog managerLog) {
try{
//获取参数类型枚举
ParamTypeEnum paramTypeEnum = managerLog.paramType();
//json
if (ParamTypeEnum.PARAM_JSON.getCode().equals(paramTypeEnum.getCode())){
String jsonStr = (String) point.getArgs()[0];
dealJson(point,result,managerLog,jsonStr);
}
//map
else if (ParamTypeEnum.PARAM_MAP.getCode().equals(paramTypeEnum.getCode())){
dealMap(point,result,managerLog);
}
//k-v键值对
else if (ParamTypeEnum.PARAM_KAK.getCode().equals(paramTypeEnum.getCode())){
dealKak(point,result,managerLog);
}
//k-v键值对 与 json 混合
else if (ParamTypeEnum.PARAM_KAK_JSON.getCode().equals(paramTypeEnum.getCode())){
dealKakAndJson(point,result,managerLog);
}
//k-v键值对 与 map 混合
else if (ParamTypeEnum.PARAM_KAK_MAP.getCode().equals(paramTypeEnum.getCode())){
dealKakAndMap(point,result,managerLog);
}
else if (ParamTypeEnum.PARAM_BEAN.getCode().equals(paramTypeEnum.getCode())){
String jsonStr = JSONObject.toJSONString(point.getArgs()[0]);
dealJson(point,result,managerLog,jsonStr);
}
}catch (Exception e){
System.out.println("ManagerLogAspect切面记录日志时发生错误!"+e.getMessage());
log.error("ManagerLogAspect切面记录日志时发生错误!"+e.getMessage());
}
return result;
}
/**
* 发送消息至kafka
* @param desc
* @throws Exception
*/
private void sendMsg(String desc) throws Exception{
kafkaManagerLogSenderUtil.sendManagerMsg(desc);
}
/**
* 处理参数类型为k1-v1,k2-v2,...,map的数据
* @param point
* @param result
* @param managerLog
* @throws Exception
*/
private void dealKakAndMap(JoinPoint point, Object result, ManagerLog managerLog) throws Exception{
int index = managerLog.mulIndex();
if (index==-1){
throw new Exception("在@ManagerLog注解中,未标明map的位置");
}
Object[] args = point.getArgs();
String targetValue = null;
Map<String,Object> map = (Map)point.getArgs()[index];
String[] paramNames = getParamNames(point);
StringBuilder sb = new StringBuilder();
sb.append("#managerLog#\n执行了如下操作:\n");
for (int i=0;i<args.length;i++){
sb.append(paramNames[i]).append(":").append(args[i].toString()).append("\n");
}
if (map != null){
for (String key : map.keySet()){
sb.append(key).append(":").append(map.get(key).toString()).append("\n");
}
}
//追加执行结果
appendResult(sb,result,targetParamName);
sendMsg(sb.toString());
}
/**
* 处理参数类型为k1-v1,k2-v2,...,json的数据
* @param point
* @param result
* @param managerLog
* @throws Exception
*/
private void dealKakAndJson(JoinPoint point, Object result, ManagerLog managerLog) throws Exception{
Object[] args = point.getArgs();
String jsonStr = (String)args[index];
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
if (managerLog.mulJson()){
jsonObject = jsonObject.getJSONObject(managerLog.childJsonName());
}
String[] paramNames = getParamNames(point);
StringBuilder sb = new StringBuilder();
sb.append("#managerLog#\n执行了如下操作:\n");
for (int i=0;i<args.length;i++){
sb.append(paramNames[i]).append(":").append(args[i].toString()).append("\n");
}
if (jsonObject != null){
for (Map.Entry<String,Object> entry : jsonObject.entrySet()){
sb.append(entry.getKey()).append(":").append(entry.getValue()).append("\n");
}
}
//追加执行结果
appendResult(sb,result,targetParamName);
sendMsg(sb.toString());
}
/**
* 处理参数类型为k-v的数据
* @param point
* @param result
* @param managerLog
*/
private void dealKak(JoinPoint point, Object result, ManagerLog managerLog) throws Exception{
StringBuilder sb = new StringBuilder();
sb.append("#managerLog#\n执行了如下操作:\n");
Object[] args = point.getArgs();
String[] paramNames = getParamNames(point);
for (int i=0;i<args.length;i++){ sb.append(paramNames[i]).append(":").append(args[i].toString()).append("\n");
}
appendResult(sb,result,targetParamName);
sendMsg(sb.toString());
}
/**
* 获取参数名
* @param point
* @return
*/
private String[] getParamNames(JoinPoint point){
Signature signature = point.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
String[] paramNames = methodSignature.getParameterNames();
if (paramNames == null){
paramNames = new String[0];
}
return paramNames;
}
/**
* 处理参数类型为map的数据
* @param point
* @param result
* @param managerLog
*/
private void dealMap(JoinPoint point, Object result, ManagerLog managerLog) throws Exception{
Map<String,Object> map = (Map)point.getArgs()[0];
StringBuilder sb = new StringBuilder();
sb.append("#managerLog#\n执行了如下操作:\n");
if (map != null){
for (String key : map.keySet()){
sb.append(key).append(":").append(map.get(key).toString()).append("\n");
}
}
//追加执行结果
appendResult(sb,result,targetParamName);
sendMsg(sb.toString());
}
/**
* 处理参数类型为json的数据
* @param point
* @param result
* @param managerLog
*/
private void dealJson(JoinPoint point, Object result, ManagerLog managerLog,String jsonStr) throws Exception{
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
if (managerLog.mulJson()){
jsonObject = jsonObject.getJSONObject(managerLog.childJsonName());
}
StringBuilder sb = new StringBuilder();
sb.append("#managerLog#\n执行了如下操作:\n");
if (jsonObject != null){
for (Map.Entry<String,Object> entry : jsonObject.entrySet()){
sb.append(entry.getKey()).append(":").append(entry.getValue()).append("\n");
}
}
//追加执行结果
appendResult(sb,result,targetParamName);
sendMsg(sb.toString());
}
/**
* 获取执行结果
* @param sb
* @param result
* @return
*/
private String appendResult(StringBuilder sb,Object result){
return appendResult(sb,result,null);
}
//targetParamName和targetParamValue是业务相关的东西,不需要关注
/**
* 获取执行结果
* @param sb
* @param result
* @param targetParamName
* @return
*/
private String appendResult(StringBuilder sb,Object result,String targetParamName){
String targetValue = "-1";
try {
sb.append("执行的结果如下:\n");
ReturnMsg returnMsg = (ReturnMsg)result;
if (null != returnMsg) {
if (returnMsg.getCode() == 0) {
sb.append(returnMsg.getData());
if (targetParamName != null){
try {
//从执行结果中获取targetParamName对应的值
targetValue = getTargetValue(returnMsg,targetParamName);
}catch (Exception e){
System.out.println("从执行结果中获取targetParamName对应的值异常"+e.getMessage());
targetValue = "-1";
}
}
} else {
sb.append(!StringUtils.isEmpty(returnMsg.getException()) ? returnMsg.getException() : returnMsg.getMsg());
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("获取执行结果发生异常"+e.getMessage());
}
return targetValue;
}
/**
* 从执行结果中获取targetParamName对应的值
* @param returnMsg
* @param targetParamName
* @return
*/
private String getTargetValue(ReturnMsg returnMsg,String targetParamName){
String targetValue = "0";
String jsonString = JSONObject.toJSONString(returnMsg.getData());
JSONObject jsonObject = JSONObject.parseObject(jsonString);
for (Map.Entry<String,Object> entry : jsonObject.entrySet()){
if (targetParamName.equals(entry.getKey())){
targetValue = entry.getValue().toString();
}
}
return targetValue;
}
}
6、在controller的方法上添加该自定义注解
@ManagerLog(paramType = ParamTypeEnum.PARAM_JSON)
@PostMapping("/single")
public ReturnMsg<Object> singleBook(@RequestBody String body) throws Exception{
}