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自定义日志注解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot日志注解与缓存优化详解

    目录 日志注解: 缓存的优化: 总结 日志注解: 关于SpringBoot中的日志处理,在之前的文章中页写过: 点击进入 这次通过注解+Aop的方式来实现日志的输出: 首先需要定义一个注解类:  @Target(ElementType.METHOD)  @Retention(RetentionPolicy.RUNTIME)  @Documented  public @interface LogAnnotation {      String module() default "";  

  • Springboot-注解-操作日志的实现方式

    此组件解决的问题是: 「谁」在「什么时间」对「什么」做了「什么事」 本组件目前针对 Spring-boot 做了 Autoconfig,如果是 SpringMVC,也可自己在 xml 初始化 bean 使用方式 基本使用 maven依赖添加SDK依赖 <dependency> <groupId>io.github.mouzt</groupId> <artifactId>bizlog-sdk</artifactId> <version>

  • springboot自定义日志注解的实现

    前言 在之前的日志记录的写法中,我们大多是写一个工具类,在这个类里面定义日志保存的方法,然后再controller中执行请求的时候调用即可,虽然调用仅仅一行代码,但是不够友好:所有可以写一个类似于@Controller等的注解,在需要保存日志的方法上面加上一个注解,这样不用在每个都写一端代码:话不多说上代码 1.首先一个log的实体类,这个无关紧要 package com.sysmg.system.domain;   import java.io.Serializable; import jav

  • 支持SpEL表达式的自定义日志注解@SysLog介绍

    目录 序言 预期 思路 过程 结果 序言 之前封装过一个日志注解,打印方法执行信息,功能较为单一不够灵活,近来兴趣来了,想重构下,使其支持表达式语法,以应对灵活的日志打印需求. 该注解是方法层面的日志打印,如需更细的粒度,还请手撸log.xxx(). 预期 通过自定义注解,灵活的语法表达式,拦截自定义注解下的方法并打印日志 日志要支持以下内容: 方法执行时间 利用已知信息(入参.配置.方法),书写灵活的日志SpEL表达式 打印方法返回结果 按照指定日志类型打印日志 思路 定义自定义注解 拦截自定

  • SpringBoot 自定义注解异步记录复杂日志详解

    目录 1.背景 2.技术方案-自定义注解 2.1 注解介绍 2.2 元注解 2.3 实现自定义注解 3.技术方案-AOP切面 3.1 AOP术语解析 3.2 切入点表达式 3.3 ADVICE通知类型 3.4 技术实现 3.5 相关操作 4.高级操作 1.背景 最近接手一个任务,需要给当前项目加一个较为复杂的日志.有多复杂呢? 要有日志类型.不同日志类型要有不同的操作和备注等.作为小白的我最开始的做法是在业务层写代码记录日志,好处就是方便,坏处就是这种做法直接侵袭Service层,Service

  • 使用Springboot自定义注解,支持SPEL表达式

    目录 Springboot自定义注解,支持SPEL表达式 1.自定义注解 2.使用AOP拦截方法,解析注解参数 自定义注解结合切面和spel表达式 自定义一个注解 自定义一个service类,在需要拦截的方法上加上@Log注解 写一个自定义切面 pom文件的依赖 测试 增加内容 Springboot自定义注解,支持SPEL表达式 举例,自定义redis模糊删除注解 1.自定义注解 import java.lang.annotation.ElementType; import java.lang.

  • SpringBoot自定义注解开发指南

    目录 一.Java注解(Annotation) 1.JDK基本注解 2.JDK元注解 二.自定义注解开发 1.含义 2.演示 三.完成切面日志操作 四.完成前端响应反应 总结 一.Java注解(Annotation) 含义:Java注解是附加在代码中的一些元信息,用于一些工具在编译. 运行时进行解析和使用,起到说明.配置的功能. 1.JDK基本注解 @Override ——>重写 @Deprecated ——>已过时 @SuppressWarnings(value = "unchec

  • java SpringBoot自定义注解,及自定义解析器实现对象自动注入操作

    # java-SpringBoot自定义参数解析器实现对象自动注入 解析器逻辑流程图表 后台解析注解的解析器 首先,我在java后台编写了一个解析器,代码如下 import com.ruoyi.framework.interceptor.annotation.LoginUser; import com.ruoyi.project.WebMoudle.WebUser.domain.WebUser; import com.ruoyi.project.WebMoudle.WebUser.service

  • SpringBoot自定义注解使用读写分离Mysql数据库的实例教程

    需求场景 为了防止代码中有的SQL慢查询,影响我们线上主数据库的性能.我们需要将sql查询操作切换到从库中进行.为了使用方便,将自定义注解的形式使用. mysql导入的依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency&

  • SpringBoot自定义注解实现Token校验的方法

    1.定义Token的注解,需要Token校验的接口,方法上加上此注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementTyp

  • SpringBoot自定义注解API数据加密和签名校验

    api数据数据签名(MD5,SHA1) 签名枚举类SginEnum.java package com.jx.app.api.framework.annotation.enums; /** * @ClassName: SginEnum * @Description: TODO(这是一个签名枚举类) * @author gangyu * @date 2018年11月20日 下午4:30:44 */ public enum SginEnum { //0不需要签名,1使用MD5数据加密 2 使用SHA数

  • springboot实现将自定义日志格式存储到mongodb中

    步骤如下 首先定义封装所需要的日志信息的实体类 public class MyLog { //根据需要定义字段 @Id private String id; private Date ts; private String level; private String msg; private String thread; //属性的get/set方法(略) } 定义操作mongodb的接口 @Repository public interface LogRepository extends Mon

随机推荐