首先我們需要了解AOP和反射的原理,我這裡主要是實戰的開發,所以就隻對AOP和反射進行簡單的概述。
AOP指的是面向切面進行編程,就是正對某一個平面進行豎向的切割,生活中的例子就好比我們每次吃飯前都要洗手一樣,這個洗手的動作就是我們需要在切面進行的方法,而吃飯前就是類似一個切面。
反射指的是利用類加載器加載的類對象反射出該類的屬性,方法和注解。比如說我想買個華為手機的電池,可是我又不知道該買怎麼樣的電池,就可以打電話給華為官網的客服小姐姐,她就會告訴你電池的型号,并且會提供給你具體的購買渠道和地址,這就是一個簡單的小例子。
1.我們需要引入支持AOP編程的jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
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();
}
@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自由打印,我這裡不需要就沒打印了。
//返回通知
@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);
}
}
}
}
}
}
效果圖如下:
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!