利用AOP列印接口入參和出參(利用AOP列印接口入參和出參)
2023-09-20 05:57:07
首先我們需要了解AOP和反射的原理,我這裡主要是實戰的開發,所以就只對AOP和反射進行簡單的概述。
AOP指的是面向切面進行編程,就是正對某一個平面進行豎向的切割,生活中的例子就好比我們每次吃飯前都要洗手一樣,這個洗手的動作就是我們需要在切面進行的方法,而吃飯前就是類似一個切面。
反射指的是利用類加載器加載的類對象反射出該類的屬性,方法和註解。比如說我想買個華為手機的電池,可是我又不知道該買怎麼樣的電池,就可以打電話給華為官網的客服小姐姐,她就會告訴你電池的型號,並且會提供給你具體的購買渠道和地址,這就是一個簡單的小例子。
1.我們需要引入支持AOP編程的jar包
org.springframework.boot spring-boot-starter-aop
2.新建一個LogAspectConfiguration配置類,加上支持面向切面的aspect註解
@Component//spring 組件註解@Aspect//支持面向切面的註解@Slf4j//lombor的日誌註解public class LogAspectConfiguration { String controllerName;//保存我們請求的controller類的類名 String method;//保存我們請求的方法名 }
3.建立我們需要切入的切點已經切入的範圍
@Component//spring 組件註解@Aspect//支持面向切面的註解@Slf4j//lombor的日誌註解public class LogAspectConfiguration { String controllerName;//保存我們請求的controller類的類名 String method;//保存我們請求的方法名 @PointCut("execution(public * com. *.*(..))")//建立切點並標註範圍 public void webLog;}
開始建立我們的請求建言(俗稱通知),我這裡指使用了before和afterreturn,因為只需要列印請求參數和返回參數,其實還有after,afterthrows和around三種啦。@Component//spring 組件註解@Aspect//支持面向切面的註解 聲明是一個切面@Slf4j//lombor的日誌註解public class LogAspectConfiguration { String controllerName;//保存我們請求的controller類的類名 String method;//保存我們請求的方法名 @PointCut("execution(public * com. *.*(..))")//建立切點並標註範圍 public void webLog; @Before("webLog")//在請求方法之前訪問 public void before(JoinPoint joinPoint) { } @AfterReturn("webLog")//在請求方法之後訪問 public void afterReturn(JoinPoint joinPoint, Object obj) { }}
開始寫我們對請求在方法調用之前進行邏輯處理和日誌列印,這裡我就不墨跡了,詳細的作用我會在用注釋進行說明@Before("logWeb")public void before(JoinPoint joinPoint){ ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes; HttpServletRequest request = attributes.getRequest;//獲取httpservletrequest //反射獲取目標類的全名 controllerName = joinPoint.getTarget.getClass.getName;//通過目標類反射獲取目標類的全名 controllerName = controllerName.substring(controllerName.lastIndexOf(".") 1,controllerName.length);//截取全名 獲得當前包的類名 //利用路勁查找處理的方法 method = request.getRequestURL.toString; if(method.endsWith("html")){ method = method.substring(method.lastIndexOf("/") 1,method.lastIndexOf(".")); }else{ method = method.substring(method.lastIndexOf("/") 1,method.length); } log.info("準備進入" controllerName "類下" method "方法"); //獲取請求的所有參數 Object[] objects = joinPoint.getArgs; for(Object obj:objects){ //去除參數中的LinkedHashMap if(obj instanceof LinkedHashMap){ continue; } if(obj!=null){ //將對象轉成json格式 並進行日誌的列印 JSONObject result = JSONObject.fromObject(obj); log.info("請求" controllerName "類下" method "方法的參數為;" result); } } }
這裡會有一個疑問,為什麼我需要單獨去除參數裡面的linkedhashmap呢?其實在springMVC中,spring會默認的幫我們在請求目標對象的參數數組中建立一個linkedhashmap,用於存放返回的值,他加入的值大家應該都很熟悉,就是model和map,由於是請求過來的參數,所以我們直接把他過濾掉。如果你們的請求參數中包含list,請再添加一個判斷obj instanceof list,想列印請求的路徑和一些其他的請求信息,可以通過request自由列印,我這裡不需要就沒列印了。
書寫放回方法之後的參數列印,這裡我們需要注意一點,springMVC的方法放回一般有兩種,一種是spring放回的是一個頁面,參數通常放在model裡面,還有一種是用@responsebody返回的一個對象(當然實際上轉化為json格式的string),我們利用放射判斷是否獲取到@repsonsebody這個註解來判斷兩中情況//返回通知@AfterReturning(value = "logWeb,returning = "retVal")public void after(JoinPoint joinPoint,Object retVal) { log.info("已經完成" controllerName "類下" method "方法的調用"); //反射獲取類加載器加載的目標對象類 Class objectClass = joinPoint.getTarget.getClass; //反射獲取目標對象所有的可見方法 Method[] methods = objectClass.getDeclaredMethods; Method methodReal = null;//前提你映射的mapper和類名必須保持一致 才可判斷類型 for (Method classMethod : methods) { if (classMethod.getName.equals(method)) { methodReal = classMethod; break; } } Object object = methodReal.getAnnotation(ResponseBody.class); //判斷是否是responsebody標籤註解的類 if(object!=null){ //存在repsonsebody註解 則直接進行將整個放回值進行列印 JSONArray result = JSONArray.fromObject(retVal); }else{ //不存在,則將model裡面的所有參數進行列印 Object[] objects = joinPoint.getArgs; for(Object obj:objects){ if(obj instanceof LinkedHashMap){ Iterator interator = ((LinkedHashMap) obj).keySet.iterator; while(interator.hasNext){ Object key = interator.next; //判斷是否為驗證對象 if(key.toString.contains("BindingResult")){ continue; } Object value = ((LinkedHashMap) obj).get(key); //如果放回值包含list 則進行list的json轉化 if(value instanceof List){ JSONArray arrayResult = JSONArray.fromObject(value); log.info("請求完成後" controllerName "類下" method "方法的返回參數為;" arrayResult); }else{ JSONObject result = JSONObject.fromObject(value); log.info("請求完成後" controllerName "類下" method "方法的返回參數為;" result); } } } } }}
效果圖如下: