有這麼一個需求:記錄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{
}