springboot自定義日志註解的實現

前言

      在之前的日志記錄的寫法中,我們大多是寫一個工具類,在這個類裡面定義日志保存的方法,然後再controller中執行請求的時候調用即可,雖然調用僅僅一行代碼,但是不夠友好;所有可以寫一個類似於@Controller等的註解,在需要保存日志的方法上面加上一個註解,這樣不用在每個都寫一端代碼;話不多說上代碼

     1、首先一個log的實體類,這個無關緊要    

package com.sysmg.system.domain;
 
import java.io.Serializable;
import java.util.Date;
 
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
 
import com.sysmg.common.annotation.ExportConfig;
 
@Table(name = "t_log")
public class SysLog implements Serializable {
 
    private static final long serialVersionUID = -8878596941954995444L;
 
    @Id
    @GeneratedValue(generator = "JDBC")
    @Column(name = "ID")
    private Long id;
 
    @Column(name = "USERNAME")
    @ExportConfig(value = "操作用戶")
    private String username;
 
    @Column(name = "OPERATION")
    @ExportConfig(value = "描述")
    private String operation;
 
    @Column(name = "TIME")
    @ExportConfig(value = "耗時(毫秒)")
    private Long time;
 
    @Column(name = "METHOD")
    @ExportConfig(value = "操作方法")
    private String method;
 
    @Column(name = "PARAMS")
    @ExportConfig(value = "參數")
    private String params;
 
    @Column(name = "IP")
    @ExportConfig(value = "IP地址")
    private String ip;
 
    @Column(name = "CREATE_TIME")
    @ExportConfig(value = "操作時間", convert = "c:com.sysmg.common.util.poi.convert.TimeConvert")
    private Date createTime;
 
    @Column(name = "LOCATION")
    @ExportConfig(value = "地點")
    private String location;
    
    // 用於搜索條件中的時間字段
    @Transient
    private String timeField;
 
    /**
     * @return ID
     */
    public Long getId() {
        return id;
    }
 
    /**
     * @param id
     */
    public void setId(Long id) {
        this.id = id;
    }
 
    /**
     * @return USERNAME
     */
    public String getUsername() {
        return username;
    }
 
    /**
     * @param username
     */
    public void setUsername(String username) {
        this.username = username == null ? null : username.trim();
    }
 
    /**
     * @return OPERATION
     */
    public String getOperation() {
        return operation;
    }
 
    /**
     * @param operation
     */
    public void setOperation(String operation) {
        this.operation = operation == null ? null : operation.trim();
    }
 
    /**
     * @return TIME
     */
    public Long getTime() {
        return time;
    }
 
    /**
     * @param time
     */
    public void setTime(Long time) {
        this.time = time;
    }
 
    /**
     * @return METHOD
     */
    public String getMethod() {
        return method;
    }
 
    /**
     * @param method
     */
    public void setMethod(String method) {
        this.method = method == null ? null : method.trim();
    }
 
    /**
     * @return PARAMS
     */
    public String getParams() {
        return params;
    }
 
    /**
     * @param params
     */
    public void setParams(String params) {
        this.params = params == null ? null : params.trim();
    }
 
    /**
     * @return IP
     */
    public String getIp() {
        return ip;
    }
 
    /**
     * @param ip
     */
    public void setIp(String ip) {
        this.ip = ip == null ? null : ip.trim();
    }
 
    /**
     * @return CREATE_TIME
     */
    public Date getCreateTime() {
        return createTime;
    }
 
    /**
     * @param createTime
     */
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
 
    public String getLocation() {
        return location;
    }
 
    public void setLocation(String location) {
        this.location = location;
    }
 
    public String getTimeField() {
        return timeField;
    }
 
    public void setTimeField(String timeField) {
        this.timeField = timeField;
    }
    
 
}

  2、定義一個註解接口

package com.sysmg.common.annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

@Target(ElementType.METHOD)代表是方法上的註解,當然也可以是類註解,字段註解等

@Target(ElementType.TYPE) //接口、類、枚舉、註解 
@Target(ElementType.FIELD) //字段、枚舉的常量 
@Target(ElementType.METHOD) //方法 
@Target(ElementType.PARAMETER) //方法參數 
@Target(ElementType.CONSTRUCTOR) //構造函數 
@Target(ElementType.LOCAL_VARIABLE)//局部變量 
@Target(ElementType.ANNOTATION_TYPE)//註解 
@Target(ElementType.PACKAGE) ///包 

@Retention(RetentionPolicy.RUNTIME)代表註解會被jvm保留,這個參數有三種

RetentionPolicy.SOURCE —— 這種類型的Annotations隻在源代碼級別保留,編譯時就會被忽略 
RetentionPolicy.CLASS —— 這種類型的Annotations編譯時被保留,在class文件中存在,但JVM將會忽略 
RetentionPolicy.RUNTIME —— 這種類型的Annotations將被JVM保留,所以他們能在運行時被JVM或其他使用反射機制的代碼所讀取和使用。
一般默認第三種

當然也可以寫

@Documented和@Order(優先級  數字越小優先級越高)

@Documented 註解表明這個註解應該被 javadoc工具記錄. 默認情況下,javadoc是不包括註解的. 但如果聲明註解時指定瞭 @Documented,則它會被 javadoc 之類的工具處理, 所以註解類型信息也會被包括在生成的文檔中。

@Order標記定義瞭組件的加載順序,這個標記包含一個value屬性。屬性接受整形值。如:1,2 等等。值越小擁有越高的優先級。Ordered.HIGHEST_PRECEDENCE這個屬性值是最高優先級的屬性,它的值是-2147483648,對應的最低屬性值是Ordered.LOWEST_PRECEDENCE,它的值是2147483647。

String value() default ""

這個代表是要傳遞的參數,類似

@Autowired(required=true)
 
 
public @interface Autowired {
 
    /**
     * Declares whether the annotated dependency is required.
     * <p>Defaults to {@code true}.
     */
    boolean required() default true;
 
}

springmvc項目還需要開啟切面編程

<aop:aspectj-autoproxy proxy-target-class="true"/>

springboot默認是開啟的

3、定義註解的實現類,也就是這個註解要做什麼事

package com.sysmg.common.aspect;
 
import java.lang.reflect.Method;
import java.util.Date;
 
import javax.servlet.http.HttpServletRequest;
 
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
 
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sysmg.common.annotation.Log;
import com.sysmg.common.util.AddressUtilsBak;
import com.sysmg.common.util.HttpContextUtils;
import com.sysmg.common.util.IPUtils;
import com.sysmg.system.domain.SysLog;
import com.sysmg.system.domain.User;
import com.sysmg.system.service.LogService;
 
@Aspect
@Component
public class LogAspect {
 
    @Autowired
    private LogService logService;
 
    @Autowired
    ObjectMapper mapper;
 
    @Pointcut("@annotation(com.sysmg.common.annotation.Log)")
    public void pointcut() {
    }
 
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) {
        Object result = null;
        long beginTime = System.currentTimeMillis();
        try {
            result = point.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        long time = System.currentTimeMillis() - beginTime;
        saveLog(point, time);
        return result;
    }
 
    private void saveLog(ProceedingJoinPoint joinPoint, long time) {
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SysLog log = new SysLog();
        Log logAnnotation = method.getAnnotation(Log.class);
        if (logAnnotation != null) {
            log.setOperation(logAnnotation.value());
        }
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        log.setMethod(className + "." + methodName + "()");
        Object[] args = joinPoint.getArgs();
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNames = u.getParameterNames(method);
        if (args != null && paramNames != null) {
            String params = "";
            for (int i = 0; i < args.length; i++) {
                params += "  " + paramNames[i] + ": " + args[i];
            }
            log.setParams(params);
        }
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        log.setIp(IPUtils.getIpAddr(request));
        log.setUsername(user.getUsername());
        log.setTime(time);
        log.setCreateTime(new Date());
        log.setLocation(AddressUtilsBak.getRealAddressByIP(log.getIp(), mapper));
        this.logService.save(log);
    }
}

這裡的實現類中日志添加的方法中使用的淘寶的獲取ip服務,後續會加上去,其實這裡面可以隨便寫一個實現方法,因為我是留工具備份,所以記錄的較多

具體的@Aspect、@Pointcut、@Around、@Before、@After等aop相關的註解和參數需要自己去鞏固一下知識

4、然後就可以在你想要記錄日志的地方使用即可

package com.sysmg.controller;
 
import java.util.List;
import java.util.Map;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
 
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.sysmg.common.annotation.Log;
import com.sysmg.common.domain.QueryRequest;
import com.sysmg.common.domain.ResponseBo;
import com.sysmg.common.util.FileUtils;
 
@Controller
public class TestController{
 
    @Log("規則")
    @RequestMapping("test")
    public String index() {
        return "test";
    }
}    

大概這樣就可以使用瞭,目前裡面缺少aop相關知識的介紹以及獲取訪問ip的工具類,不過跟本課題關系不大,後續會更上去

到此這篇關於springboot自定義日志註解的實現的文章就介紹到這瞭,更多相關springboot自定義日志註解內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: