Spring AOP - 判断方法是否被@Scheduled调用
Spring AOP - Determine whether method was invoked by @Scheduled
我有一个运行时注解@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
}
也许您想实现这样的目标:
@Slf4j
@Component
public class ScheduledTask {
@Scheduled(cron = "0/1 * * * * *")
@ScheduledTaskAnnotation(message = "ScheduledTaskMessage", number = 10)
public void doAction() {
log.debug("Task scheduled");
}
}
@Slf4j
@Aspect
@Component
public class ScheduledTaskAspect {
@Around("execution(public * *(..)) && @annotation(hu.gaszabo.sample.schedule.ScheduledTaskAnnotation)")
public void logScheduledTaskAction(final ProceedingJoinPoint p) {
log.debug("Aspect");
parameters(p).ifPresent(a -> {
log.debug("message: {}", a.message());
log.debug("number: {}", a.number());
});
try {
p.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
}
private Optional<ScheduledTaskAnnotation> parameters(final ProceedingJoinPoint p) {
final Method method = ((MethodSignature) p.getSignature()).getMethod();
return Optional.ofNullable(AnnotationUtils.findAnnotation(method, ScheduledTaskAnnotation.class));
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD })
public @interface ScheduledTaskAnnotation {
String message() default "Message";
long number() default 0L;
}
检查堆栈跟踪总是很丑陋,但你当然可以做到:
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
或应用程序 class.
触发的
我有一个运行时注解@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
}
也许您想实现这样的目标:
@Slf4j
@Component
public class ScheduledTask {
@Scheduled(cron = "0/1 * * * * *")
@ScheduledTaskAnnotation(message = "ScheduledTaskMessage", number = 10)
public void doAction() {
log.debug("Task scheduled");
}
}
@Slf4j
@Aspect
@Component
public class ScheduledTaskAspect {
@Around("execution(public * *(..)) && @annotation(hu.gaszabo.sample.schedule.ScheduledTaskAnnotation)")
public void logScheduledTaskAction(final ProceedingJoinPoint p) {
log.debug("Aspect");
parameters(p).ifPresent(a -> {
log.debug("message: {}", a.message());
log.debug("number: {}", a.number());
});
try {
p.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
}
private Optional<ScheduledTaskAnnotation> parameters(final ProceedingJoinPoint p) {
final Method method = ((MethodSignature) p.getSignature()).getMethod();
return Optional.ofNullable(AnnotationUtils.findAnnotation(method, ScheduledTaskAnnotation.class));
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD })
public @interface ScheduledTaskAnnotation {
String message() default "Message";
long number() default 0L;
}
检查堆栈跟踪总是很丑陋,但你当然可以做到:
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
或应用程序 class.