通过 AOP(面向切面编程)技术来拦截所有使用 @Scheduled 注解的方法,并在这些方法执行前后进行日志记录

通过 AOP(面向切面编程)技术来拦截所有使用 @Scheduled 注解的方法,并在这些方法执行前后进行日志记录。具体记录了定时任务的开始和结束时间,以及是否成功执行。如果任务执行失败,它还会记录异常信息。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
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.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.lang.reflect.Method;
import java.util.Objects;

@Aspect
@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ScheduleAspect extends AbstractPointcutAdvisor {

    @Override
    public Pointcut getPointcut() {
        return Objects.requireNonNull(AnnotationMatchingPointcut.forMethodAnnotation(Scheduled.class));
    }

    @Override
    public Advice getAdvice() {
        return new ScheduleAdvice();
    }

    public static class ScheduleAdvice implements Advice, MethodInterceptor {

        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            Method method = invocation.getMethod();
            TimeWatcher timeWatcher = new TimeWatcher();
            boolean success = true;
            String traceId = StringUtilsCustom.uuidStr();
            Class<?> targetClass = invocation.getThis() != null ? invocation.getThis().getClass() : null;
            Logger logger = targetClass != null ? LoggerFactory.getLogger(targetClass) : log;

            try { 
                logStart(logger, method, targetClass);
                timeWatcher.begin();
                return invocation.proceed();
            } catch (Throwable th) {
                success = false;
                logError(logger, method, targetClass, th);
                throw th; // Re-throw the exception to ensure it's properly handled
            } finally {
                logEnd(logger, method, targetClass, timeWatcher, success); 
            }
        }

        private void logStart(Logger logger, Method method, Class<?> targetClass) {
            if (targetClass != null) {
                logger.info("开始执行定时任务:{}#{}", targetClass.getName(), method.getName());
            }
        }

        private void logError(Logger logger, Method method, Class<?> targetClass, Throwable th) {
            if (targetClass != null) {
                logger.error("定时任务执行失败:{}#{}", targetClass.getName(), method.getName(), th);
            }
        }

        private void logEnd(Logger logger, Method method, Class<?> targetClass, TimeWatcher timeWatcher, boolean success) {
            if (targetClass != null) {
                String status = success ? "成功" : "失败";
                double duration = timeWatcher.end() / 1000.0;
                logger.info("结束定时任务{}:{}#{},用时:{}s", status, targetClass.getName(), method.getName(), duration);
            }
        }
    }
}

class TimeWatcher {
    private long startTime;

    public void begin() {
        this.startTime = System.currentTimeMillis();
    }

    public long end() {
        return System.currentTimeMillis() - startTime;
    }
}

class StringUtilsCustom {
    public static String uuidStr() {
        return java.util.UUID.randomUUID().toString();
    }
}

主要作用

  1. 日志记录:在定时任务开始和结束时记录日志,包含任务名称、执行时间和执行状态(成功或失败)。
  2. 异常处理:在任务执行失败时记录错误信息。
  3. 性能监控:记录定时任务的执行时间。

详细解释

  1. 定义切面(Aspect)

    • @Component:将 ScheduleAspect 类声明为 Spring 容器中的一个 Bean。
  2. 继承 AbstractPointcutAdvisor

    • 继承 AbstractPointcutAdvisor 类,创建一个自定义的切面 Advisor。
  3. 定义切入点(Pointcut)

    • getPointcut() 方法返回一个切入点,拦截所有使用 @Scheduled 注解的方法。
    • 使用 AnnotationMatchingPointcut.forMethodAnnotation(Scheduled.class) 创建切入点。
  4. 定义通知(Advice)

    • getAdvice() 方法返回一个自定义的 ScheduleAdvice 对象,该对象实现了 MethodInterceptor 接口。
  5. 方法拦截逻辑(Method Interceptor Logic)

    • ScheduleAdvice 类实现了 MethodInterceptor 接口,通过 invoke 方法拦截方法调用。
    • invoke(MethodInvocation invocation) 方法在方法执行前后执行自定义逻辑。
  6. 日志记录和异常处理

    • 使用 Logger 记录定时任务的开始和结束信息。
    • 记录任务执行时间,使用 TimeWatcher 对象测量执行时间。
    • 记录任务执行成功或失败的状态。

代码的意义

这段代码对于监控和调试定时任务非常有用:

  • 监控定时任务的执行情况:通过日志记录任务的开始和结束时间,可以监控定时任务的执行频率和时长。
  • 调试定时任务:在任务执行失败时记录错误信息,帮助开发人员快速定位问题。
  • 性能分析:记录任务的执行时间,便于分析和优化定时任务的性能。

关键点总结

  • 切面(Aspect):拦截定时任务方法。
  • 通知(Advice):在方法执行前后进行日志记录和异常处理。
  • 日志记录:记录方法的开始、结束时间和执行状态。
  • 异常处理:在方法执行失败时记录异常信息。
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值