74
74
import org .springframework .scheduling .support .CronTrigger ;
75
75
import org .springframework .scheduling .support .ScheduledMethodRunnable ;
76
76
import org .springframework .util .Assert ;
77
+ import org .springframework .util .ClassUtils ;
77
78
import org .springframework .util .StringUtils ;
78
79
import org .springframework .util .StringValueResolver ;
79
80
@@ -122,6 +123,12 @@ public class ScheduledAnnotationBeanPostProcessor
122
123
public static final String DEFAULT_TASK_SCHEDULER_BEAN_NAME = "taskScheduler" ;
123
124
124
125
126
+ /**
127
+ * Reactive Streams API present on the classpath?
128
+ */
129
+ private static final boolean reactiveStreamsPresent = ClassUtils .isPresent (
130
+ "org.reactivestreams.Publisher" , ScheduledAnnotationBeanPostProcessor .class .getClassLoader ());
131
+
125
132
protected final Log logger = LogFactory .getLog (getClass ());
126
133
127
134
private final ScheduledTaskRegistrar registrar ;
@@ -402,13 +409,63 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
402
409
protected void processScheduled (Scheduled scheduled , Method method , Object bean ) {
403
410
// Is the method a Kotlin suspending function? Throws if true and the reactor bridge isn't on the classpath.
404
411
// Does the method return a reactive type? Throws if true and it isn't a deferred Publisher type.
405
- if (ScheduledAnnotationReactiveSupport .isReactive (method )) {
412
+ if (reactiveStreamsPresent && ScheduledAnnotationReactiveSupport .isReactive (method )) {
406
413
processScheduledAsync (scheduled , method , bean );
407
414
return ;
408
415
}
409
416
processScheduledSync (scheduled , method , bean );
410
417
}
411
418
419
+ /**
420
+ * Process the given {@code @Scheduled} method declaration on the given bean,
421
+ * as a synchronous method. The method must accept no arguments. Its return value
422
+ * is ignored (if any), and the scheduled invocations of the method take place
423
+ * using the underlying {@link TaskScheduler} infrastructure.
424
+ * @param scheduled the {@code @Scheduled} annotation
425
+ * @param method the method that the annotation has been declared on
426
+ * @param bean the target bean instance
427
+ * @see #createRunnable(Object, Method)
428
+ */
429
+ private void processScheduledSync (Scheduled scheduled , Method method , Object bean ) {
430
+ Runnable task ;
431
+ try {
432
+ task = createRunnable (bean , method );
433
+ }
434
+ catch (IllegalArgumentException ex ) {
435
+ throw new IllegalStateException ("Could not create recurring task for @Scheduled method '" +
436
+ method .getName () + "': " + ex .getMessage ());
437
+ }
438
+ processScheduledTask (scheduled , task , method , bean );
439
+ }
440
+
441
+ /**
442
+ * Process the given {@code @Scheduled} bean method declaration which returns
443
+ * a {@code Publisher}, or the given Kotlin suspending function converted to a
444
+ * {@code Publisher}. A {@code Runnable} which subscribes to that publisher is
445
+ * then repeatedly scheduled according to the annotation configuration.
446
+ * <p>Note that for fixed delay configuration, the subscription is turned into a blocking
447
+ * call instead. Types for which a {@code ReactiveAdapter} is registered but which cannot
448
+ * be deferred (i.e. not a {@code Publisher}) are not supported.
449
+ * @param scheduled the {@code @Scheduled} annotation
450
+ * @param method the method that the annotation has been declared on, which
451
+ * must either return a Publisher-adaptable type or be a Kotlin suspending function
452
+ * @param bean the target bean instance
453
+ * @see ScheduledAnnotationReactiveSupport
454
+ */
455
+ private void processScheduledAsync (Scheduled scheduled , Method method , Object bean ) {
456
+ Runnable task ;
457
+ try {
458
+ task = ScheduledAnnotationReactiveSupport .createSubscriptionRunnable (method , bean , scheduled ,
459
+ this .registrar ::getObservationRegistry ,
460
+ this .reactiveSubscriptions .computeIfAbsent (bean , k -> new CopyOnWriteArrayList <>()));
461
+ }
462
+ catch (IllegalArgumentException ex ) {
463
+ throw new IllegalStateException ("Could not create recurring task for @Scheduled method '" +
464
+ method .getName () + "': " + ex .getMessage ());
465
+ }
466
+ processScheduledTask (scheduled , task , method , bean );
467
+ }
468
+
412
469
/**
413
470
* Parse the {@code Scheduled} annotation and schedule the provided {@code Runnable}
414
471
* accordingly. The Runnable can represent either a synchronous method invocation
@@ -419,7 +476,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean)
419
476
* @param method the method that the annotation has been declared on
420
477
* @param bean the target bean instance
421
478
*/
422
- protected void processScheduledTask (Scheduled scheduled , Runnable runnable , Method method , Object bean ) {
479
+ private void processScheduledTask (Scheduled scheduled , Runnable runnable , Method method , Object bean ) {
423
480
try {
424
481
boolean processedSchedule = false ;
425
482
String errorMessage =
@@ -543,54 +600,6 @@ protected void processScheduledTask(Scheduled scheduled, Runnable runnable, Meth
543
600
}
544
601
}
545
602
546
- /**
547
- * Process the given {@code @Scheduled} method declaration on the given bean,
548
- * as a synchronous method. The method must accept no arguments. Its return value
549
- * is ignored (if any), and the scheduled invocations of the method take place
550
- * using the underlying {@link TaskScheduler} infrastructure.
551
- * @param scheduled the {@code @Scheduled} annotation
552
- * @param method the method that the annotation has been declared on
553
- * @param bean the target bean instance
554
- * @see #createRunnable(Object, Method)
555
- */
556
- protected void processScheduledSync (Scheduled scheduled , Method method , Object bean ) {
557
- Runnable task ;
558
- try {
559
- task = createRunnable (bean , method );
560
- }
561
- catch (IllegalArgumentException ex ) {
562
- throw new IllegalStateException ("Could not create recurring task for @Scheduled method '" + method .getName () + "': " + ex .getMessage ());
563
- }
564
- processScheduledTask (scheduled , task , method , bean );
565
- }
566
-
567
- /**
568
- * Process the given {@code @Scheduled} bean method declaration which returns
569
- * a {@code Publisher}, or the given Kotlin suspending function converted to a
570
- * {@code Publisher}. A {@code Runnable} which subscribes to that publisher is
571
- * then repeatedly scheduled according to the annotation configuration.
572
- * <p>Note that for fixed delay configuration, the subscription is turned into a blocking
573
- * call instead. Types for which a {@code ReactiveAdapter} is registered but which cannot
574
- * be deferred (i.e. not a {@code Publisher}) are not supported.
575
- * @param scheduled the {@code @Scheduled} annotation
576
- * @param method the method that the annotation has been declared on, which
577
- * must either return a Publisher-adaptable type or be a Kotlin suspending function
578
- * @param bean the target bean instance
579
- * @see ScheduledAnnotationReactiveSupport
580
- */
581
- protected void processScheduledAsync (Scheduled scheduled , Method method , Object bean ) {
582
- Runnable task ;
583
- try {
584
- task = ScheduledAnnotationReactiveSupport .createSubscriptionRunnable (method , bean , scheduled ,
585
- this .registrar ::getObservationRegistry ,
586
- this .reactiveSubscriptions .computeIfAbsent (bean , k -> new CopyOnWriteArrayList <>()));
587
- }
588
- catch (IllegalArgumentException ex ) {
589
- throw new IllegalStateException ("Could not create recurring task for @Scheduled method '" + method .getName () + "': " + ex .getMessage ());
590
- }
591
- processScheduledTask (scheduled , task , method , bean );
592
- }
593
-
594
603
/**
595
604
* Create a {@link Runnable} for the given bean instance,
596
605
* calling the specified scheduled method.
0 commit comments