1. 程式人生 > >Springboot AOP處理日誌資訊錄入

Springboot AOP處理日誌資訊錄入

現在凡是企業級的或者稍微大點專案,基本都需要日誌管理. 我這邊在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配置,現在都是幾個註解搞定的事.