使用AOP(面向切面编程)结合自定义注解来实现在MongoDB添加日志功能,需要执行以下步骤:
学习更多MongoDB操作可以看这一篇SpringBoot操作Mongodb
导入依赖
以下两个是aop和MongoDB的依赖
<!-- aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-mongodb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
yml配置文件
spring:
data:
mongodb:
uri: mongodb://192.168.10.26:27017/mylog
----------------------------------------------------------
如果使用properties就是用如下 192.168.10.26为ip 27017为端口 mylog为表名自己改即可
spring.data.mongodb.uri=mongodb://192.168.10.26:27017/mylog
实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class OperationLog {
private Long id; //日志id
private String userCode; //操作人
private String ip; //操作ip
private String type; //操作类型
private String description; //操作名称
private String model; //操作模块
private String operationTime; //操作时间
private String result; //操作结果
}
定义自定义注解
你需要定义一个自定义注解,用于标记需要添加日志的方法。
import java.lang.annotation.*;
@Target(ElementType.METHOD)//注解放置的目标位置即方法级别
@Retention(RetentionPolicy.RUNTIME)//注解在哪个阶段执行
@Documented
public @interface OperationLogAnnotation {
String operModul() default ""; // 操作模块
String operType() default ""; // 操作类型
String operDesc() default ""; // 操作说明
}
创建切面类
接下来,你需要创建一个切面类,它将包含在执行方法前后添加日志的逻辑。可以按照以下示例创建切面类:
import cn.hutool.core.util.IdUtil;
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.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.ParseException;
/**
* 操作日志切面处理类
*/
@Aspect
@Component
public class OperationLogAspect {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 设置操作日志切入点 在注解位置切入代码
*/
@Pointcut("@annotation(OperationLogAnnotation)")
public void operLogPointcut() {
}
/**
* 记录操作日志
*/
@AfterReturning(returning = "result", value = "operLogPointcut()")
public void saveOperLog(JoinPoint joinPoint, Object result) throws ParseException {
// 获取RequestAttributes 直接获取ThreadLocal里面的值,这样就保证了每一次获取到的Request是该请求的request.
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();//RequestContextHolder既是持有上下文的Request容器
// 从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
try {
//将返回值转换成map集合
// HttpResult map = (HttpResult) result;
//创建日志对象
OperationLog operationLog = new OperationLog();
//设置id
operationLog.setId(IdUtil.getSnowflake().nextId());
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//MethodSignature 包含了被拦截方法的一些信息,如目标方法的返回类型,目标方法的参数列表信息
//获取切入点所在的方法
Method method = signature.getMethod();
//获取操作
OperationLogAnnotation annotation = method.getAnnotation(OperationLogAnnotation.class);
if (annotation != null) {
operationLog.setModel(annotation.operModul());
operationLog.setType(annotation.operType());
operationLog.setDescription(annotation.operDesc());
}
//操作时间
operationLog.setOperationTime(MongoUtils.getNowTime());
//操作用户
// String jwt = request.getHeader("jwt");
// Map payLoad = JWTUtil.getPayload(jwt);
// String username = (String) payLoad.get("username");
//用户信息如何加密的 就如何解密 获取用户信息
operationLog.setUserCode("用户待完善");
//操作IP
operationLog.setIp(IPUtil.getRemoteHost(request));//获取真实ip
//返回值信息
// operationLog.setResult(map.getMsg());
//保存日志
mongoTemplate.insert(operationLog);
} catch (Exception e) {
e.printStackTrace();
}
}
}
工具类
/**
* @program: SpringBoot_MongoDB
* @author: 阿水
* @create: 2023-06-08 16:52
**/
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* mongodb工具类
*/
public class MongoUtils {
/**
* 获取MongoDB的当前时间,时区UTC转GMT
* @return GMT时区时间
*/
public static String getNowTime() {
Date currentDate = new Date();
// 创建日期格式化对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
// 格式化日期对象为字符串
String formattedDate = sdf.format(currentDate);
// 输出当前年月日时分秒以及毫秒
// System.out.println("当前时间:" + formattedDate);
return formattedDate;
}
}
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* @Description
* @Author 刘品水
* @Data 2023/5/16 20:12
StringUtils都差不多 看着选你习惯用的就好
*/
public class IPUtil {
/**
* 获取目标主机的ip
*/
public static String getRemoteHost(HttpServletRequest request) {
String Xip = request.getHeader("X-Real-IP");
String XFor = request.getHeader("X-Forwarded-For");
if (StringUtils.isNotBlank(XFor) && !"unKnown".equalsIgnoreCase(XFor)) {
// 多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = XFor.indexOf(",");
if (index != -1) {
return XFor.substring(0, index);
} else {
return XFor;
}
}
XFor = Xip;
if (StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)) {
return XFor;
}
if (StringUtils.isBlank(XFor) || "unKnown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || "unKnown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || "unKnown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isBlank(XFor) || "unKnown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isBlank(XFor) || "unKnown".equalsIgnoreCase(XFor)) {
XFor = request.getRemoteAddr();
}
return XFor;
}
}