我可以在@Scheduled 中添加上下文吗?

Can I add context in @Scheduled?

这是我的代码:

 @Scheduled(cron = "30 3 * * * *")
    public void myCron() {
        //we don't care what we do here
    }

我想知道是否可以在我的 @Scheduled 上自动添加跟踪 ID(或其他信息)。

每次@Scheduled触发的id都不一样

我想这样做是为了避免重复代码,例如:

@Scheduled(cron = "10 3 * * * *")
public void myCron() {
    MDC.put("myId", UUID.randomUUID().toString());
    //we don't care what we do here
}
@Scheduled(cron = "30 3 * * * *")
public void mySecondCron() {
    MDC.put("myId", UUID.randomUUID().toString());
    //we don't care what we do here
}

我厌倦了实现 SchedulingConfigurer 但是 SchedulingConfigurer#configureTasks 来不及在任务上添加行为因为任务(可运行)已经创建

谢谢

您可以尝试实现自定义 TaskScheduler 并在 SchedulingConfigurer.configureTasks 中注册。 ConcurrentTaskScheduler可以作为一个例子。不幸的是,这个 class 不是为继承而设计的,否则 decorateTask 方法将受到保护。因此,您需要重写所有方法以使用您的逻辑添加一个额外的 Runnable 装饰器。像这样的东西:

@Configuration
@EnableScheduling
public class ScheduledConfig implements SchedulingConfigurer {

    public static class MyTaskScheduler extends ConcurrentTaskScheduler {

        public MyTaskScheduler() {
        }

        public MyTaskScheduler(ScheduledExecutorService scheduledExecutor) {
            super(scheduledExecutor);
        }

        public MyTaskScheduler(Executor concurrentExecutor, ScheduledExecutorService scheduledExecutor) {
            super(concurrentExecutor, scheduledExecutor);
        }

        // TODO override other methods

        @Override
        public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
            return super.schedule(decorateTask(task), trigger);
        }

        private Runnable decorateTask(Runnable task) {
            // not 100% sure about safety of this cast
            return new MyRunnable((ScheduledMethodRunnable) task);
        }

        private static class MyRunnable implements Runnable {

            private final ScheduledMethodRunnable runnable;
            private final AtomicLong counter = new AtomicLong();

            public MyRunnable(ScheduledMethodRunnable runnable) {
                this.runnable = runnable;
            }

            @Override
            public void run() {
                System.out.println(runnable.getMethod().toGenericString() + " " + counter.incrementAndGet());
                runnable.run();
            }
        }
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        TaskScheduler taskScheduler = new MyTaskScheduler(Executors.newScheduledThreadPool(10));
        taskRegistrar.setTaskScheduler(taskScheduler);
    }

    @Scheduled(cron = "0/1 * * * * *")
    public void test() {
        System.out.println("Running task in thread " + Thread.currentThread().getId());
    }
}

您还可以使用 AOP 在代码进入任何用 @Scheduled 注释的方法之前拦截代码并设置 MDC。为了更好地衡量,它会在退出该方法时清除 MDC。

注意 atExecutionTimeInMyNamespace 切入点,您可以在其中选择放入自己的包命名空间以限制仅在您自己的代码中使用 @Scheduled(即排除它在任何第 3 方库中的使用,但不太可能可能是)。

@Aspect
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ScheduledTaskTracingAspect {

  @Pointcut("@annotation(org.springframework.scheduling.annotation.Scheduled)")
  public void methodAnnotatedWithScheduled() {}

  @Pointcut("execution(* com.mycompany..*(..))")
  public void atExecutionTimeInMyNamespace() {}

  @Around("methodAnnotatedWithScheduled() && atExecutionTimeInMyNamespace()")
  public Object connectionAdvice(ProceedingJoinPoint joinPoint) throws Throwable {

    MDC.put("myId", UUID.randomUUID().toString());

    try {
      return joinPoint.proceed();
    }
    finally {
      // Might as well clear all the MDC, not just the "myId"
      MDC.clear();
    }
  }

}