Springboot AOP處理日誌資訊錄入
阿新 • • 發佈:2019-01-02
現在凡是企業級的或者稍微大點專案,基本都需要日誌管理. 我這邊在springboot基礎上做了個日誌資訊記錄到資料庫的功能,在這裡備份一下,以後有需要就省的再重寫了.
首先我們得準備好所需要的jar,當然了這裡是pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency >
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
這裡的web是最基本的,後面也會用到的.
其實日誌管理實現的方式有很多種,攔截器,aop切面等等,我這邊用的就是aop切面實現的.既然要用到切面實現,那就必須要有切點,我這邊是以自定義註解為切點(當然也可以切到指定路徑下的資料夾哦)
下面為自定義切點:
package com.jshh.busness.LogAOP;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 這個註解是用來規定註解的作用範圍的,這裡定義為method方法級別.
@Retention(RetentionPolicy.RUNTIME) // 這個註解可以理解為定義註解的生命週期,這裡標識一直存在(編譯和執行之後)
public @interface LogAnnotation {
// 定義註解引數
public String operateContent() default "";
public String operateType() default "";
}
這邊簡單的介紹下@Target和@Retention註解吧.
@Target 用來取值 註解使用範圍:
METHOD 可用於方法上
TYPE 可用於類或者介面上
ANNOTATION_TYPE 可用於註解型別上(被@interface修飾的型別)
CONSTRUCTOR 可用於構造方法上
FIELD 可用於域上
LOCAL_VARIABLE 可用於區域性變數上
PACKAGE 用於記錄java檔案的package資訊
PARAMETER 可用於引數上
@Retention
1、RetentionPolicy.SOURCE:註解只保留在原始檔,當Java檔案編譯成class檔案的時候,註解被遺棄;
2、RetentionPolicy.CLASS:註解被保留到class檔案,但jvm載入class檔案時候被遺棄,這是預設的生命週期;
3、RetentionPolicy.RUNTIME:註解不僅被儲存到class檔案中,jvm載入class檔案之後,仍然存在;
回到正題,註解弄完了,現在要弄一個實體類來封裝所需要的日誌資訊:
import com.jshh.entity.CommenEntity;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class LogEntity implements CommenEntity {
private String uuid;
private String userid;
private String username;
private String ip;
private String status;
private String operatetype;
private String detail;
private String operatetime;
private String operatecontent;
private String subsystemid;
}
基礎準備都弄完了,現在就要去做切面的實現類了.廢話不多說程式碼附上:
package com.jshh.busness.LogAOP;
import com.alibaba.fastjson.JSON;
import com.jshh.client.DataCommenServerClient;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
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.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author 一隻會飛的豬
* @desc 日誌錄入aop實現類
* @time 2018/8/28
* */
@Component
@Aspect
public class LogAspectClass {
@Autowired
LogAddService logAddService;
// 這裡定義下切點的位置,也就是剛才我們自定義的註解.
@Pointcut("@annotation(com.jshh.busness.LogAOP.LogAnnotation)")
public void mypointcut(){}
//訊息通知 @AfterReturning,在切點方法執行之後觸發returning 為目標函式返回值
@AfterReturning(returning = "result",value = "mypointcut()")
public void addlog(JoinPoint joinPoint,Object result){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//從切面織入點處通過反射機制獲取織入點處的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//獲取切入點所在的方法
Method method = signature.getMethod();
String operatetype =""; // 定義操作方式
String operatecontent=""; // 定義操作內容
// 獲取註解中的操作方式
if(method!=null&&!"".equals(method)){
// 獲取自定義註解操作
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
// 獲取使用者操作方式
operatetype = logAnnotation.operateType();
// 獲取使用者操作內容
operatecontent = logAnnotation.operateContent();
}
// 獲取請求的類名
String classname = joinPoint.getTarget().getClass().getName();
// 獲取請求的方法名
String methodname = classname+"."+method.getName();
// 獲取請求方式
String Method = request.getMethod();
// 獲取請求url
String URL = request.getRequestURI().toString();
// 獲取請求的ip地址
String IP = request.getRemoteAddr();
// 獲取userid
String userid = request.getParameter("userid");
// 獲取子系統id
String subsystemid = request.getParameter("subsystemid");
// 生成uuid
String uuid = UUID.randomUUID().toString();
// 獲取請求的引數
String argsname[] = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
Map<String,Object> parammap = new HashMap<>();
if(argsname.length>0){
parammap= getParam(joinPoint,argsname,methodname);
}
String detail = JSON.toJSONString(parammap);
// 獲取操作狀態
Map<String,Object> statusmap = new HashMap<>();
String status="";
statusmap = (Map<String, Object>) result;
Integer code= (Integer)statusmap.get("code");
if(code==0){
status = "失敗";
}else{
status = "成功";
}
// 日誌實體類封裝
LogEntity logEntity = new LogEntity();
logEntity.setUserid(userid);
logEntity.setUuid(uuid);
logEntity.setIp(IP);
logEntity.setStatus(status);
logEntity.setOperatetype(operatetype);
logEntity.setOperatecontent(operatecontent);
logEntity.setDetail(detail);
logEntity.setSubsystemid(subsystemid);
logAddService.addLogInfo(logEntity);
System.out.println("aop+++++++++++++++++++++切面++++++++++++++++++++");
System.out.println("使用者操作方式:----------"+operatetype);
System.out.println("使用者操作內容:----------"+operatecontent);
System.out.println("請求方式:-------------"+Method);
System.out.println("請求地址url:----------"+IP+URL);
System.out.println("請求ip地址:------------"+IP);
System.out.println("請求引數:------------"+request.getParameterNames().toString());
System.out.println("請求引數:============"+request.getQueryString());
System.out.println("請求方法名:============"+methodname);
System.out.println("請求userid:============"+userid);
System.out.println("返回引數:============"+detail);
System.out.println("返回結果狀態:============"+status);
System.out.println("返回結果:============"+logEntity.toString());
}
// 處理引數格式,並返回需要的引數
public static Map<String, Object> getParam(JoinPoint joinPoint,String argsname[],String methodname) {
Map<String,Object> detailmap = new HashMap<>();
Map<String, Object> map = new HashMap<>();
Map<String, Object> mapCODE = new HashMap<>();
// 獲取引數值
Object args[] = joinPoint.getArgs();
// 獲取引數名
argsname = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
String paramsString = "";
for(int i=0; i < argsname.length; i++) {
if (!argsname[i].equals("model")) {
map.put(argsname[i], args[i]);
}
}
detailmap.put("method",methodname);
detailmap.put("params",map);
System.out.println("detail:====="+detailmap.toString());
return detailmap;
}
}
最後新增一個controller:
@GetMapping("queryRoleByUserid")
@LogAnnotation(operateType = "角色",operateContent = "根據使用者id查詢角色相關資訊")
public Model queryRoleByUserid(Model model, @RequestParam("userid") String userid) throws Exception {
boolean ret = true;
String msg = "";
Object result = null;
try {
result = userService.queryRoleByUserid(userid);
} catch (Exception e) {
ret = false;
msg = e.getMessage();
e.printStackTrace();
}
model = buildModel(model, ret, result, msg);
return model;
}
至於儲存資料的方法這個就不用多說了吧,該咋儲存就咋儲存,除了這個方法,其他程式碼拷貝就能用的哦.
整個操作,不需要編寫什麼配置檔案的,以前的ssm的xml配置,現在都是幾個註解搞定的事.