2019独角兽企业重金招聘Python工程师标准>>>
首先,定义日志注解,注解字段可自行扩展
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Log {/**操作名称*/String value() default "";/**模块名*/String moduleName() default "";}
主要思路是:
利用AOP拦截被注解标注的方法,进行相关参数获取。为了防止日志保存影响正常的业务执行,因此利用Spring的事件机制,发送事件给监听器,监听器收到事件后,异步保存日志。
注:在本示例中,使用Lombok注解,请自行了解Lombok注解作用。
下面是Aop的实现类,拿到拦截参数后,关键的一步是通过spring 的事件机制,将事件广播出去。
LogAop.java
@Slf4j
@Aspect
@Component
public class LogAop {@Pointcut(value = "@annotation(com.chillax.boot.core.common.annotation.Log)")public void cutService() {}@Around("cutService()")public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {long startTime = System.currentTimeMillis();//先执行业务Object result = point.proceed();long endTime = System.currentTimeMillis();try {handle(point, endTime - startTime, null);} catch (Exception e) {log.error("日志记录出错!", e);}return result;}@AfterThrowing(pointcut = "cutService()", throwing = "e")public void doAfterThrowing(JoinPoint point, Throwable e) {try {handle(point, null, e);} catch (Exception ex) {log.error("日志记录出错!", ex);}}private void handle(JoinPoint point, Long methodProcessTime, Throwable e) throws Exception {//获取拦截的方法名Signature sig = point.getSignature();MethodSignature msig = null;if (!(sig instanceof MethodSignature)) {throw new IllegalArgumentException("该注解只能用于方法");}msig = (MethodSignature) sig;Object target = point.getTarget();Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());String methodName = currentMethod.getName();//获取拦截方法的参数Object[] params = point.getArgs();//获取操作名称Log annotation = currentMethod.getAnnotation(Log.class);String moduleName = annotation.moduleName();String operationName = annotation.value();//这里根据自己的业务需求,封装自己的业务类SysLog sysLog = new SysLog();SpringContextUtil.publishEvent(new SysLogEvent(sysLog));}}
日志事件类,用于事件间的传递。可以扩展此类用做其他用途。
SysLogEvent.java
public class SysLogEvent extends ApplicationEvent {public SysLogEvent(Object source) {super(source);}}
Spring容器工具类,在项目启动时,注入Spring上下文,然后封装一下事件发送方法及其他常用方法。
SpringContextUtil.java
@Slf4j
@Service
@Lazy(false)
public class SpringContextUtil implements ApplicationContextAware, DisposableBean {private static ApplicationContext applicationContext;public static Object getBean(String name) {return applicationContext.getBean(name);}public static
}
最后,编写事件监听器。
@Async 标注此方法执行为异步,需要使用**@EnableAsync**注解开启此功能
@Order标记此监听器为最低级别加载
@EventListener(SysLogEvent.class) 标注监听的事件
比较重要的是,监听器需要将它加入到Spring容器中。通过event.getSource() 获取到发送事件时,传递的对象。
SysLogListener.java
@AllArgsConstructor
@Slf4j
@Component
public class SysLogListener {private final ISysLogService sysLogService;@Async@Order@EventListener(SysLogEvent.class)public void saveSysLog(SysLogEvent event) {SysLog sysLog = (SysLog) event.getSource();sysLogService.save(sysLog);}
}