Spring Boot项目利用AOP获取日志记录

本文详细介绍如何在SpringBoot项目中使用AOP和自定义注解实现方法级别的日志记录,包括自定义注解的创建、日志AOP类的设计、IP地址和JSON工具类的实现,以及日志实体类的保存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思路总结:首先在需要做日志记录的方法中添加一个自定义注解,再去实现一个日志AOP类,AOP类把自定义注解设置为切点,所以当系统执行某一个添加了自定义注解的方法时,AOP会自动获取该方法名称以及用户信息实现日志记录。

需要依赖包

<!--spring切面aop依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

一.编写自定义注解

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;

/**
 * 自定义注解类  定义controller方法的中文含义
 * @Target({METHOD,TYPE}) 表示这个注解可以用用在类/接口上,还可以用在方法上
 * @Retention(RetentionPolicy.RUNTIME) 表示这是一个运行时注解,即运行起来之后,才获取注解中的相关信息,而不像基本注解如@Override 那种不用运行,在编译时eclipse就可以进行相关工作的编译时注解。
 * @Inherited 表示这个注解可以被子类继承
 * @Documented 表示当执行javadoc的时候,本注解会生成相关文档
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Operation {
    String value() default "";
}

日志AOP类会用到获取IP地址的工具类,也用到json工具类,实现工具类如下

二、实现必要工具类

2.1获取ip地址工具类

import javax.servlet.http.HttpServletRequest;

/**
* 获取用户真实的ip地址
* @param request
* @return
*/
public class IpAdrressUtil {

	public static String getIpAdrress(HttpServletRequest request) {
	 	String ip = null;
	
	    //X-Forwarded-For:Squid 服务代理
	    String ipAddresses = request.getHeader("X-Forwarded-For");
	    String unknown = "unknown";
	    if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
	        //Proxy-Client-IP:apache 服务代理
	        ipAddresses = request.getHeader("Proxy-Client-IP");
	    }
	
	    if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
	        //WL-Proxy-Client-IP:weblogic 服务代理
	        ipAddresses = request.getHeader("WL-Proxy-Client-IP");
	    }
	
	    if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
	        //HTTP_CLIENT_IP:有些代理服务器
	        ipAddresses = request.getHeader("HTTP_CLIENT_IP");
	    }
	
	    if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
	        //X-Real-IP:nginx服务代理
	        ipAddresses = request.getHeader("X-Real-IP");
	    }
	
	    //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
	    if (ipAddresses != null && ipAddresses.length() != 0) {
	        ip = ipAddresses.split(",")[0];
	    }
	
	    //还是不能获取到,最后再通过request.getRemoteAddr();获取
	    if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
	        ip = request.getRemoteAddr();
	    }
	    return ip;
	}


}

2.2实现json工具类

import java.util.Map;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JacksonUtil {

    private final static ObjectMapper objectMapper = new ObjectMapper();

    private JacksonUtil() {

    }

    public static ObjectMapper getInstance() {
        return objectMapper;
    }

    /**
     * javaBean、列表数组转换为json字符串
     */
    public static String obj2json(Object obj) throws Exception {
        return objectMapper.writeValueAsString(obj);
    }

    /**
     * json 转JavaBean
     */

    public static <T> T json2pojo(String jsonString, Class<T> clazz) throws Exception {
        objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        return objectMapper.readValue(jsonString, clazz);
    }

    /**
     * json字符串转换为map
     */
    public static <T> Map<String, Object> json2map(String jsonString) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(Include.NON_NULL);//JsonInclude.Include.NON_NULL
        return mapper.readValue(jsonString, Map.class);
    }
}

三、实现日志AOP类


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

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
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 org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.xuan.cncert.entity.SysLog;
import com.xuan.cncert.service.SysLogService;
import com.xuan.cncert.util.IpAdrressUtil;
import com.xuan.cncert.util.JacksonUtil;
import com.xuan.cncert.util.Operation;


/**
 * 系统日志:切面处理类
 */
@Aspect
@Component
public class SysLogAspect {

    @Autowired
    private SysLogService sysLogService;

    //定义切点 @Pointcut
    //在注解的位置切入代码
    @Pointcut("@annotation(com.xuan.cncert.util.Operation)")
    public void logPoinCut() {
    }

    //切面 配置通知
    @AfterReturning("logPoinCut()")
    public void saveSysLog(JoinPoint joinPoint) {
        //保存日志
        SysLog sysLog = new SysLog();

        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();


        //获取操作
        Operation operation = method.getAnnotation(Operation.class);
        if (operation != null) {
            String value = operation.value();
            sysLog.setOperation(value);//保存获取的操作
        }

        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();

        //获取请求的方法名
        String methodName = method.getName();
        sysLog.setMethod(className + "." + methodName);

        //请求的参数
        Object[] args = joinPoint.getArgs();
        //将参数所在的数组转换成json
        String params = null;
        try {
            params = JacksonUtil.obj2json(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        sysLog.setParams(params);

        //请求的时间
        sysLog.setCreattime(new Date());

        //获取用户ip地址
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        sysLog.setIp(IpAdrressUtil.getIpAdrress(request));

        //调用service保存SysLog实体类到数据库
        sysLogService.addSysLog(sysLog);
    }

}

最后,我是调用service保存SysLog实体类到数据库,你也可以直接输出到控制台,要保存到数据库,还要实现service类,mapper类和javeBean。
我就简单贴个javaBean,其他类就不具体贴出了。

mport java.io.Serializable;
import java.util.Date;

public class SysLog implements Serializable {
    private Long id;

    private String username; //用户名

    private String operation; //操作

    private String method; //方法名

    private String params; //参数

    private String ip; //ip地址

    private Date creattime; //操作时间
    //创建getter和setter方法

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getOperation() {
		return operation;
	}

	public void setOperation(String operation) {
		this.operation = operation;
	}

	public String getMethod() {
		return method;
	}

	public void setMethod(String method) {
		this.method = method;
	}

	public String getParams() {
		return params;
	}

	public void setParams(String params) {
		this.params = params;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public Date getCreattime() {
		return creattime;
	}

	public void setCreattime(Date creattime) {
		this.creattime = creattime;
	}

}

javaBean中使用lombok的注解实现了get、set等方法。

以下是存进数据库的数据,关于IP的问题是因为本地测试,部署在服务器上就会有正常的IP地址了。

代码技术参考文章https://malizhi.cn/SpringBoot_Log/#more

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值