Spring Boot利用自定义注解记录请求或方法执行日志

Spring Boot利用自定义注解记录请求或方法执行日志

首先,定义日志注解,注解字段可自行扩展

@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 <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext.getBean(name, requiredType);
    }

    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    public static Class<? extends Object> getType(String name) {
        return applicationContext.getType(name);
    }

    public static void publishEvent(ApplicationEvent event) {
        if (applicationContext != null) {
            applicationContext.publishEvent(event);
        }
    }

    public static void clearHolder() {
        if (log.isDebugEnabled()) {
            log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
        }
        applicationContext = null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }
    @Override
    public void destroy() throws Exception {
        SpringContextUtil.clearHolder();
    }
}

最后,编写事件监听器。

@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);
    }
}

转载于:https://my.oschina.net/u/3226414/blog/3001771

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值