一、首先自定义注解类SysLogAnnotation
/**
* @date 2019/2/1 操作日志注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLogAnnotation {
/**
* 描述
* @return {String}
*/
String value();
}
二、添加切面类SysLogAspect,实现同一的日志添加
1、使用@Aspect注解定义切面类,@Component注册bean,@Around("@annotation(sysLogAnnotation)")实现切面环绕监听添加了@sysLogAnnotation注解的方法,通过sysLogAnnotation.value()获取注解中的value值
@Aspect
@Slf4j
@Component
public class SysLogAspect {
@Around("@annotation(sysLogAnnotation)")
@SneakyThrows
public Object around(ProceedingJoinPoint point, com.example.swaggerdemo.annotation.SysLogAnnotation sysLogAnnotation) {
String strClassName = point.getTarget().getClass().getName();
String strMethodName = point.getSignature().getName();
log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);
SysLog logVo = SysLogUtils.getSysLog();
logVo.setTitle(sysLogAnnotation.value());
// 发送异步日志事件
Long startTime = System.currentTimeMillis();
Object obj;
try {
obj = point.proceed();
}
catch (Exception e) {
logVo.setType(LogTypeEnum.ERROR.getType());
logVo.setException(e.getMessage());
throw e;
}
finally {
Long endTime = System.currentTimeMillis();
logVo.setTime(String.valueOf(endTime - startTime));
SpringContextHolder.publishEvent(new SysLogEvent(logVo));
}
return obj;
}
}
@Slf4j
@UtilityClass
public class SysLogUtils {
public SysLog getSysLog() {
//获取请求url,ip,httpMethod
HttpServletRequest request = ((ServletRequestAttributes) Objects
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
SysLog sysLog = new SysLog();
//sysLog.setCreateBy(Objects.requireNonNull(getUsername()));
//sysLog.setUpdateBy(Objects.requireNonNull(getUsername()));
sysLog.setType(LogTypeEnum.NORMAL.getType());
sysLog.setRemoteAddr(ServletUtil.getClientIP(request));
sysLog.setRequestUri(URLUtil.getPath(request.getRequestURI()));
sysLog.setMethod(request.getMethod());
sysLog.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
sysLog.setParams(HttpUtil.toParams(request.getParameterMap()));
//sysLog.setServiceId(getClientId(request));
return sysLog;
}
}
2、通过继承ApplicationEvent 自定义事件SysLogEvent 。
public class SysLogEvent extends ApplicationEvent {
public SysLogEvent(SysLog source) {
super(source);
}
}
3、通过实现ApplicationContextAware接口获得自定义上下文管理器,调用applicationContext.publishEvent(event)方法发布事件,在切面监听方法时,调用事件发布方法发布事件。
@Slf4j
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
if (log.isDebugEnabled()) {
log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
}
applicationContext = null;
}
/**
* 发布事件
* @param event
*/
public static void publishEvent(ApplicationEvent event) {
if (applicationContext == null) {
return;
}
applicationContext.publishEvent(event);
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
@SneakyThrows
public void destroy() {
SpringContextHolder.clearHolder();
}
}
三、编写类监听事件,通过@EventListener(SysLogEvent.class)注解指定监听的时间,@Async注解异步执行事件,@RequiredArgsConstructor注解写在类上可以代替@AutoWired注解,需要注意的是在注入时需要用final定义,或者使用@notnull注解
@Slf4j
@RequiredArgsConstructor
@Component
public class SysLogListener {
private final SysLogMapper sysLogMapper;
@Async
@Order
@EventListener(SysLogEvent.class)
public void saveSysLog(SysLogEvent event) {
SysLog sysLog = (SysLog) event.getSource();
sysLogMapper.insert(sysLog);
}
}
四、在controller中的方法上加上@SysLogAnnotation注解就可以实现通过aop切面的方式来添加日志
@SysLogAnnotation("导出日志")
@ApiOperation(value = "导出测试",notes = "导出测试")
@GetMapping("export")
public Object exportTest() {
exportService.testExport();
return "success";
}