Warm tip: This article is reproduced from serverfault.com, please click

Spring AOP-确定方法是否由@Scheduled调用

(Spring AOP - Determine whether method was invoked by @Scheduled)

发布于 2020-12-21 17:07:41

我有一个运行时批注@MyAnnotation,我想编写一个Aspect来确定是否通过以下方式调用以下test()方法:

  • Spring的@Scheduled框架
  • 普通方法调用
@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Scheduled(cron = "*/1 * * * * *") // scheduled to invoke every second
    @MyAnnotation
    public void test() {
        // business logic
    }
}

方面代码(切入点+建议)

    @Around(value="@annotation(myAnnotation)")
    public Object featureToggle(ProceedingJoinPoint joinPoint, MyAnnotation myAnnotation) throws Throwable {
        Boolean isInvoked = // TODO - is invoked by @Scheduled or not
    }
Questioner
Marcus Chiu
Viewed
0
kriegaex 2020-12-22 11:08:40

检查堆栈跟踪总是很丑陋,但是你当然可以:

package de.scrum_master.spring.q65397019;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
package de.scrum_master.spring.q65397019;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MyComponent {
  @Scheduled(fixedRate = 1000)
//  @Async
  @MyAnnotation
  public void doSomething() {}
}
package de.scrum_master.spring.q65397019;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@Configuration
@EnableScheduling
@EnableAsync
public class DemoApplication {
  public static void main(String[] args) throws InterruptedException {
    try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
      doStuff(appContext);
    }
  }

  private static void doStuff(ConfigurableApplicationContext appContext) throws InterruptedException {
    MyComponent myComponent = appContext.getBean(MyComponent.class);
    myComponent.doSomething();
    Thread.sleep(1000);
    myComponent.doSomething();
  }
}
package de.scrum_master.spring.q65397019;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class MyAspect {
  @Around("@annotation(myAnnotation)")
  public Object advice2(ProceedingJoinPoint joinPoint, MyAnnotation myAnnotation) throws Throwable {
    if (
      Arrays.stream(new Exception().getStackTrace())
        .map(StackTraceElement::toString)
        .anyMatch(string -> string.contains("scheduling.support.ScheduledMethodRunnable.run("))
    )
      System.out.println(joinPoint + " -> scheduled");
    else
      System.out.println(joinPoint + " -> normal");
    return joinPoint.proceed();
  }
}

这将打印如下内容:

(...)
2020-12-22 10:00:59.372  INFO 1620 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService 'taskScheduler'
execution(void de.scrum_master.spring.q65397019.MyComponent.doSomething()) -> scheduled
2020-12-22 10:00:59.456  INFO 1620 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-12-22 10:00:59.456  INFO 1620 --- [           main] d.s.spring.q65397019.DemoApplication     : Started DemoApplication in 6.534 seconds (JVM running for 8.329)
execution(void de.scrum_master.spring.q65397019.MyComponent.doSomething()) -> normal
execution(void de.scrum_master.spring.q65397019.MyComponent.doSomething()) -> scheduled
execution(void de.scrum_master.spring.q65397019.MyComponent.doSomething()) -> normal
2020-12-22 10:01:00.475  INFO 1620 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Shutting down ExecutorService 'taskScheduler'
2020-12-22 10:01:00.477  INFO 1620 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
(...)

在Java 9+上,你可以使用堆栈漫游API,它比从异常实例创建完整的堆栈跟踪或从当前运行的线程查询它们更为有效。

警告:如果你还使用注释了计划的方法@Async,那么它将不再起作用,因为异步运行的方法没有堆栈跟踪,你可以在其中识别它是由ScheduledMethodRunnable还是应用程序类触发的