IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot自定义日志注解实现日志管理 -> 正文阅读

[Java知识库]springboot自定义日志注解实现日志管理

springBoot,AOP切面实现日志记录,自定义注解,注解属性动态传参
注解属性动态传参,包括:方法入参传参、方法体传参
方法入参传参,针对链接携带的参数进行记录
方法体传参,对处理后的数据进行记录

1、遇到的问题

1)Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut

在这里插入图片描述
解决方案,把两个入参的位置换一下就好了
在这里插入图片描述

2、自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
    String operation() default "";
    String description() default "";
}

JDK中有一些元注解,主要有@Target,@Retention,@Document,@Inherited用来修饰注解。
@Target 表明该注解可以应用的java元素类型

Target类型描述
ElementType.TYPE应用于类、接口(包括注解类型)、枚举
ElementType.TYPE应用于类、接口(包括注解类型)、枚举
ElementType.FIELD应用于属性(包括枚举中的常量)
ElementType.METHOD应用于方法
ElementType.PARAMETER应用于方法的形参
ElementType.CONSTRUCTOR应用于构造函数
ElementType.LOCAL_VARIABLE应用于局部变量
ElementType.ANNOTATION_TYPE应用于注解类型
ElementType.PACKAGE应用于包
ElementType.TYPE_PARAMETER1.8版本新增,应用于类型变量)
ElementType.TYPE_USE1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)

@Retention 表明该注解的生命周期

生命周期类型描述
RetentionPolicy.SOURCE编译时被丢弃,不包含在类文件中
RetentionPolicy.CLASSJVM加载时被丢弃,包含在类文件中,默认值
RetentionPolicy.RUNTIME由JVM 加载,包含在类文件中,在运行时可以被获取到

@Document 表明该注解标记的元素可以被Javadoc 或类似的工具文档化
@Inherited 表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解

3、aop切面(aspect)

1) 静态参数

①、切面代码

package com.meirit.dong.aspect;

import com.meirit.dong.annotation.LogAnnotation;
import com.meirit.dong.entity.LogEntity;
import com.meirit.dong.mapper.LogMapper;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@Slf4j
@Aspect
@Component
public class LogAspect {
    @Autowired
    private LogMapper logMapper;


    @Pointcut("@annotation(logger)")
    public void logAnnotation(LogAnnotation logger) {
    }

    @Around("logAnnotation(logger)")
    public Object around(ProceedingJoinPoint joinPoint, LogAnnotation logger) throws Throwable {
        //获取方法参数值数组
        Object[] args = joinPoint.getArgs();
        //得到其方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        // 记录日志
        saveLog(method);

        //获取方法参数类型数组
        Class[] paramTypeArray = methodSignature.getParameterTypes();
        //动态修改其参数
        //注意,如果调用joinPoint.proceed()方法,则修改的参数值不会生效,必须调用joinPoint.proceed(Object[] args)
        args[1] = 30; // 将num的值设置成30
        Object result = joinPoint.proceed(args);
        return result;
    }

    private void saveLog(Method method) {
        LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
        LogEntity logEntity = new LogEntity();
        logEntity.setId(UUID.randomUUID().toString());
        logEntity.setOperation(logAnnotation.operation());
        logEntity.setDescription(logAnnotation.description());
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        logEntity.setCreateTime(sf.format(new Date()));
        logMapper.insertLog(logEntity);
    }
}

②、具体应用

 @GetMapping("/test1")
    @ResponseBody
    @LogAnnotation(operation = "查询", description = "查询人员信息")
    public String test1(String name, int num) throws IOException {
        return "success" + num;
    }

③、获取注解内容的2种方式

  • 通过入参获取
    在这里插入图片描述

  • 通过自带的属性获取

 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
 Method method = methodSignature.getMethod();
 LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);

2) 动态传参

动态传参的两个核心:

① 获取通配符
② 获取通配符的值

通配符的定义如下

    /**
    *#{operation} : operation字段的通配符
    *#{description}:description字段的通配符
    * 注意:通配符的设置可自己定义,怎么定义解析的时候怎么解析
    */
    @GetMapping("/test1")
    @ResponseBody
    @LogAnnotation(operation = "#{operation}", description = "#{description}")
    public String test1(String operation, int num) throws IOException {
        AnnotationResolver.INSTANCE.params.put("description","查询人员信息");
        return "success" + num;
    }

注意:上面代码中,传递参数的方式有2种;
第一种:直接通过入参传递,如operation
第二种:在方法体里面传递,如description

代码实现如下

①、切面代码

package com.meirit.dong.aspect;

import com.meirit.dong.annotation.LogAnnotation;
import com.meirit.dong.aspect.resolver.AnnotationResolver;
import com.meirit.dong.entity.LogEntity;
import com.meirit.dong.mapper.LogMapper;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@Slf4j
@Aspect
@Component
public class LogAspect {
    @Autowired
    private LogMapper logMapper;


    @Pointcut("@annotation(com.meirit.dong.annotation.LogAnnotation)")
    public void logAnnotation() {
    }

    @Around("logAnnotation()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取方法参数值数组
        Object[] args = joinPoint.getArgs();
        //动态修改其参数
        //注意,如果调用joinPoint.proceed()方法,则修改的参数值不会生效,必须调用joinPoint.proceed(Object[] args)
        args[1] = 30; // 将num的值设置成30
        Object result = joinPoint.proceed(args);

        //得到其方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        //获取方法参数类型数组
        //Class[] paramTypeArray = methodSignature.getParameterTypes();

        // 记录日志
        saveLog(method,joinPoint);
        return result;
    }

    private void saveLog(Method method, ProceedingJoinPoint joinPoint) {
        LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
        LogEntity logEntity = new LogEntity();
        logEntity.setId(UUID.randomUUID().toString());
        Object operation = AnnotationResolver.INSTANCE.resolver(joinPoint, logAnnotation.operation());
        Object description = AnnotationResolver.INSTANCE.resolver(joinPoint, logAnnotation.description());
        logEntity.setOperation(String.valueOf(operation));
        logEntity.setDescription(String.valueOf(description));
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        logEntity.setCreateTime(sf.format(new Date()));
        logMapper.insertLog(logEntity);
    }
}

② 解析通配符代码

package com.meirit.dong.aspect.resolver;

import graphql.com.google.common.collect.Maps;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.util.Map;

public enum AnnotationResolver {
    INSTANCE;
    public Map<String,Object> params = Maps.newConcurrentMap();
    public Object resolver(JoinPoint joinPoint, String str) {
        if (str == null) {
            return null ;
        }

        Object value = null;
        if (str.matches("#\\{\\D*\\}")) {// 如果name匹配上了#{},则把内容当作变量
            String newStr = str.replaceAll("#\\{", "").replaceAll("\\}", "");
            if (newStr.contains(".")) { // 复杂类型
                try {
                    value = complexResolver(joinPoint, newStr);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                value = simpleResolver(joinPoint, newStr);
            }
        } else { //非变量
            value = str;
        }
        return value;
    }
    private Object complexResolver(JoinPoint joinPoint, String str) throws Exception {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        String[] names = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        String[] strs = str.split("\\.");

        for (int i = 0; i < names.length; i++) {
            if (strs[0].equals(names[i])) {
                Object obj = args[i];
                Method dmethod = obj.getClass().getDeclaredMethod(getMethodName(strs[1]), null);
                Object value = dmethod.invoke(args[i]);
                return getValue(value, 1, strs);
            }
        }
        return null;
    }

    private Object getValue(Object obj, int index, String[] strs) {
        try {
            if (obj != null && index < strs.length - 1) {
                Method method = obj.getClass().getDeclaredMethod(getMethodName(strs[index + 1]), null);
                obj = method.invoke(obj);
                getValue(obj, index + 1, strs);
            }
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private String getMethodName(String name) {
        return "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1).toUpperCase());
    }

    private Object simpleResolver(JoinPoint joinPoint, String str) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String[] names = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        Object value = null;
        if(params.keySet().contains(str)){
            value = params.get(str);
            params.remove(str);
        }else {
            for (int i = 0; i < names.length; i++) {
                if (str.equals(names[i])) {
                    value = args[i];
                    break;
                }
            }
        }
        return value;
    }

}

③、业务实现

    @GetMapping("/test1")
    @ResponseBody
    @LogAnnotation(operation = "#{operation}", description = "#{description}")
    public String test1(String operation, int num) throws IOException {
        AnnotationResolver.INSTANCE.params.put("description","查询人员信息");
        return "success" + num;
    }
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:01:38  更:2022-10-22 21:05:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年2日历 -2025/2/3 7:32:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码