前言
- 最近要对系统进行改造,要对业务中的一些接口进行记录,包括接口调用时间,参数,操作类型,最后决定以注解的方式去实现,涵盖了AOP的基本用法及常用注解,不会对原来的业务逻辑产生影响,下面介绍一下具体的实现方式。
实现
- 首先创建一个注解
package com.dev.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Version 1.0
*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
/**
* 传入需要获取的参数,可在default设置默认值
*/
String moduleName() default "内容管理";
/**
* 操作类型
*/
String operateType();
}
- 定义一个切面类,加上@Component注解,@Aspect注解加入配置管理,可以通过Hutool提供的方法获取客户端浏览器,操作系统,ip地址,使用Hutool需要引入依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.0.M2</version>
</dependency>
package com.dev.common.service;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.dev.common.annotation.OperationLog;
import com.dev.common.utils.IpUtil;
import com.dev.common.utils.ParamsUtil;
import com.dev.service.CommonService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* @Description 操作日志处理类
* @Version 1.0
*/
@Aspect
@Component
@Slf4j
public class OperateLogAspect {
@Resource
private LogDao logDao;
/** 计算操作消耗时间 */
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
/**
* 处理请求前执行
*/
@Before(value = "@annotation(operationLog)")
public void boBefore(JoinPoint joinPoint, OperationLog operationLog)
{
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
* @param operationLog 注解
*/
@AfterReturning(pointcut = "@annotation(operationLog)")
public void doAfterReturning(JoinPoint joinPoint, OperationLog operationLog)
{
handleLog(joinPoint,operationLog,null);
}
/**
* 拦截异常操作,方法异常时执行
*
* @param joinPoint 切点
* @param operationLog 注解
* @param e 异常
*/
@AfterThrowing(value = "@annotation(operationLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, OperationLog operationLog, Exception e)
{
handleLog(joinPoint, operationLog,e);
}
/**
* 封装日志
* @param joinPoint
* @param operationLog
* @param e
*/
public void handleLog(final JoinPoint joinPoint,OperationLog operationLog,final Exception e) {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
//获取请求参数
String reqParam = preHandleParams(joinPoint, request);
String userIp = IpUtil.getIpAddrPlus(request);
String uaStr = request.getHeader("User-Agent");
UserAgent userAgent = UserAgentUtil.parse(uaStr);
//计算耗时
long cost = System.currentTimeMillis() - TIME_THREADLOCAL.get();
Map<String,Object> operLogMap = new HashMap<>();
operLogMap.put("clientIp",userIp);
operLogMap.put("requestUrl",request.getRequestURI());
operLogMap.put("requestParam",request.getParameterMap().toString());
// 获取自定义注解的参数
operLogMap.put("moduleName",operationLog.moduleName());
operLogMap.put("requestDuration",cost);
// 默认成功
operLogMap.put("isSuccess",1);
operLogMap.put("errorMessage","");
// 失败回填0,错误原因
if(e != null){
operLogMap.put("isSuccess",0);
operLogMap.put("errorMessage",e.getMessage());
}
// 插入日志
logDao.insertLog(operLogMap);
} catch (Exception exp) {
exp.printStackTrace();
} finally {
TIME_THREADLOCAL.remove();
}
}
}
如果是springMVC项目还需在springMVC项目中需要开启AspectJ注解支持
<?xml encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/util/spring-util-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy/>
</beans>
- 在你的方法接口上加上自定义注解,传入一些参数,就可以使用了.
@GetMapping("/hello")
@OperationLog(moduleName = "文章管理",operateType = "插入")
public Result Hello(HttpServletRequest request){
HashMap<Object, Object> result = new HashMap<>();
result.put("code",200);
result.put("msg","success");
return Result.success(result);
}
@GetMapping("/test")
@OperationLog(moduleName = "测试管理",operateType = "更新")
public Result Hello(HttpServletRequest request){
HashMap<Object, Object> result = new HashMap<>();
result.put("code",200);
result.put("msg","success");
return Result.success(result);
}
原文链接:https://blog.csdn.net/Jnsone/article/details/129934551