一首先需要定义一个注解的interface,也就是我们自定义注解的注解名,同时也要定义里面的内部参数
package com.common.log.vo;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {
/**
* 模块
*
*/
String moduleName() default "";
String sheetType() default "";
String operationDesc() default "";
String sourceNoField() default "";
/**
* 单据编码字段名称
*
*/
String businessKeyField();
}
在SpringBoot中,有四个元注解,它们被称为注解的注解,我们在自定义注解的时候,就会用到它们,下面对他们分别做个简单的介绍:
@Target:这个注解就是规定了我们自定义的注解所使用的的范围,里面有个属性叫ElementType,关于这个属性有着很多值:
@Target(ElementType.TYPE) 接口、类
@Target(ElementType.FIELD) 属性
@Target(ElementType.METHOD) 方法
@Target(ElementType.PARAMETER) 方法参数 parameter
@Target(ElementType.CONSTRUCTOR) 构造函数constructor
@Target(ElementType.LOCAL_VARIABLE) 局部变量
@Target(ElementType.ANNOTATION_TYPE) 注解
@Target(ElementType.PACKAGE) 包
@Retention:定义注解的保留策略 有以下三种
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不含
@Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.
@Inherited:这个注解是一个标记注解,表明被标注的类型是可以被继承的。
二 写切面逻辑
在逻辑里实现对方法输出结果的捕获与处理,持久化。我们知道一个方法的输出结果无非是两种,一个是方法执行成功,输出正确的结果;一个是方法执行失败,中途报错。对于这两种不同的执行结果,分别都需要用不同的逻辑来进行处理。
package com.cpit.ycyt.psc.common.logaspect;
import com.cpit.ycyt.common.core.util.JsonUtil;
import com.cpit.ycyt.common.security.util.SecurityUtil;
import com.cpit.ycyt.psc.common.domain.PscLog;
import com.cpit.ycyt.psc.common.logaspect.vo.OperationLog;
import com.cpit.ycyt.psc.common.mapper.PscLogCommonMapper;
import com.cpit.ycyt.upms.api.domain.UserDetail;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
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.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Component
@Aspect
@Slf4j
public class OperationLogAspect {
@Autowired
SecurityUtil securityUtil;
@Autowired
PscLogCommonMapper pscLogCommonMapper;
@Pointcut(value = "@annotation(com.cpit.ycyt.psc.common.logaspect.vo.OperationLog)")
public void beanAnnotatedWithController() {
}
@Pointcut(value = "@annotation(operationLog)", argNames = "operationLog")
private void operationLogMethod(OperationLog operationLog) {
}
/**
* 管理员注解拦截操作日志
*
* @param joinPoint 切点
* @param operationLog 自定义注解
* @return Object
* @throws Throwable
*/
@Around(value = "beanAnnotatedWithController() && @annotation(operationLog)",argNames = "joinPoint,operationLog")
public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
long startTime = System.currentTimeMillis();
final Object retVal = joinPoint.proceed();
long endTime = System.currentTimeMillis();
String moduleName = operationLog.moduleName();
String sheetType = operationLog.sheetType();
String operationDesc = operationLog.operationDesc();
String businessKeyField = operationLog.businessKeyField();
String sourceNoField = operationLog.sourceNoField();
Map<String, Object> nameAndArgs = joinArgs(joinPoint);
// 处理操作人信息
UserDetail user = getUser();
String reqMsg = JsonUtil.toJSONString(nameAndArgs);
PscLog model = new PscLog();
model.setModuleName(moduleName).setSheetType(sheetType).setRequestTime(startTime).setOperationDesc(operationDesc).setSourceNo(sourceNo).setSheetNo(sheetNo)
.setReqMsg(reqMsg).setErrorMsg(JsonUtil.toJSONString(retVal)).setOperator(user.getUserCode() + "_" + user.getUserName())
.setOperatingTime((endTime - startTime) / 1000).setCreateTime(new Date());
pscLogCommonMapper.insert(model);
return retVal;
}
/**
* 管理员注解拦截操作异常日志
*
* @param joinPoint 切点
* @param operationLog 自定义注解
* @return
* @throws Throwable
*/
@AfterThrowing(value = "beanAnnotatedWithController() && operationLogMethod(operationLog)", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, OperationLog operationLog, Exception ex) {
long startTime = System.currentTimeMillis();
String moduleName = operationLog.moduleName();
String sheetType = operationLog.sheetType();
String operationDesc = operationLog.operationDesc();
String businessKeyField = operationLog.businessKeyField();
String sourceNoField = operationLog.sourceNoField();
// 处理操作人信息
UserDetail user = getUser();
String reqMsg = JsonUtil.toJSONString(nameAndArgs);
PscLog model = new PscLog();
model.setModuleName(moduleName).setSheetType(sheetType).setRequestTime(startTime).setOperationDesc(operationDesc).setSourceNo(sourceNo)
.setSheetNo(sheetNo).setReqMsg(reqMsg).setErrorMsg(ex.toString() + "," + JsonUtil.toJSONString(ex.getStackTrace()).substring(0, 500))
.setOperator(user.getUserCode() + "_" + user.getUserName()).setOperatingTime(0L).setCreateTime(new Date());
pscLogCommonMapper.insert(model);
}
/**
* 获取登录用户信息
*
* @return
*/
private UserDetail getUser() {
try {
UserDetail user = securityUtil.getUser();
if (Objects.isNull(user)) {
user = new UserDetail();
user.setUserCode("auto");
user.setUserName("auto");
}
return user;
}catch (NullPointerException e) {
// 接口调用均不带TOKEN,会报空指针异常,不进行日志记录
UserDetail user = new UserDetail();
user.setUserCode("error");
user.setUserName("error");
return user;
} catch (Exception e) {
log.error("=======日志记录切面-获取用户信息异常。异常信息:{}=======", e.getMessage());
UserDetail user = new UserDetail();
user.setUserCode("error");
user.setUserName("error");
return user;
}
}
/**
* 组合方法参数为Map
*
* @param joinPoint
* @return
*/
public static Map<String, Object> joinArgs(JoinPoint joinPoint) {
Map<String, Object> map = new HashMap<>();
Object[] objects = joinPoint.getArgs();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String[] names = methodSignature.getParameterNames();
for (int i = 0; i < names.length; i++) {
map.put(names[i], objects[i]);
}
return map;
}
}
最终在调用接口上面添加注解即可成功使用
@OperationLog(moduleName = "web", sheetType = "", operationDesc = "手动生成", businessKeyField = "sheetNo")
public HttpResult getUserInfo(@RequestParam("sheetNo") String sheetNo, @RequestParam("sheetType") String sheetType) {```