spring schedule task scheduling
Enable Spring's task scheduling function requires the @EnableScheduling annotation, which introduces ScheduledAnnotationBeanPostProcessor.
beanprocessor is a bean postprocessor that scans methods with @Scheduled annotations, converts them into executable tasks, and registers them into TaskScheduler for management and execution based on the annotation's properties.
In this way, developers only need to add @Scheduled annotation to the methods of ordinary Spring Beans, and Spring can automatically execute these methods according to the specified time strategy without manually creating and managing threads. There is a registerr inside it that ScheduledTaskRegistrar is used to register tasks.
Find @Scheduled annotation
In the() method, the specific processing method of each annotation is
public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler || bean instanceof ScheduledExecutorService) { // Ignore AOP infrastructure such as scoped proxies. return bean; } // parse all @Scheduled annotations Class<?> targetClass = (bean); if (!(targetClass) && (targetClass, (, ))) { Map<Method, Set<Scheduled>> annotatedMethods = (targetClass, (<Set<Scheduled>>) method -> { Set<Scheduled> scheduledAnnotations = ( method, , ); return (!() ? scheduledAnnotations : null); }); if (()) { (targetClass); } else { // Call the processScheduled() method to initialize the scheduling task ((method, scheduledAnnotations) -> (scheduled -> processScheduled(scheduled, method, bean))); } } return bean; }
processScheduled(Scheduled scheduled, Method method, Object bean)
-
scheduled
: Annotate the configuration task cycle related information -
method
: Annotate the method -
bean
: Annotate the instance object. If you have beans and method, you can use reflection to make method calls.
The processScheduled() method first passes the method encapsulation on the scheduled annotation to you a runnable task, and then
Encapsulation tasks
protected Runnable createRunnable(Object target, Method method) { //@Scheduled The method of annotation modification must be without parameters (() == 0, "Only no-arg methods may be annotated with @Scheduled"); Method invocableMethod = (method, ()); return new ScheduledMethodRunnable(target, invocableMethod); }
Here is to wrap the instance method into a ScheduledMethodRunnable object.
The method is to call the method through reflection.
(); ();
Register a task
The properties in the @Scheduled annotation will be parsed in the next step, such as
-
fixedRate
: Execute the task at a fixed time interval (milliseconds), waiting for the specified time after the last task begins. -
fixedDelay
: After the last task is completed, wait for the specified time (milliseconds) before executing the next task. -
cron
: Use Cron expression to define the execution time of a task. -
initialDelay
: Delay time (milliseconds) before the task is executed for the first time.
Different types will be registered through different methods of registerr.
() () ()
Here, an important class ScheduledTaskRegistrar is introduced to register tasks.
Here we take the scheduleCronTask method as an example to see the registration of cron expression type tasks:
public ScheduledTask scheduleCronTask(CronTask task) { ScheduledTask scheduledTask = (task); boolean newTask = false; if (scheduledTask == null) { scheduledTask = new ScheduledTask(task); newTask = true; } // Whether to initialize taskScheduler if ( != null) { //Create a task = ((), ()); } else {//taskScheduler is not initialized, put the task in the unprocessed list addCronTask(task); (task, scheduledTask); } return (newTask ? scheduledTask : null); }
The incoming parameter is a CronTask type, and the above processScheduled() method call instance is
/** runnable is the encapsulated ScheduledMethodRunnable */ (new CronTask(runnable, new CronTrigger(cron /**cron expression*/, timeZone)))
Finally, call ((), ()) to start the task.
taskScheduler
Before looking at the() method, first look at how taskScheduler is initialized.
This also depends on the ScheduledAnnotationBeanPostProcessor, which implements the SmartInitializingSingleton interface. AfterSingletonsInstantiated() method is called afterSingletonsInstantiated() after all Singleton Beans are initialized. This method will call finishRegistration(); to complete your registration.
private void finishRegistration() { if ( != null) { (); } if ( instanceof ListableBeanFactory) { Map<String, SchedulingConfigurer> beans = ((ListableBeanFactory) ).getBeansOfType(); List<SchedulingConfigurer> configurers = new ArrayList<>(()); (configurers); for (SchedulingConfigurer configurer : configurers) { (); } } if (() && () == null) { ( != null, "BeanFactory must be set to find scheduler by type"); try { // Search for TaskScheduler bean... (resolveSchedulerBean(, , false)); } catch (NoUniqueBeanDefinitionException ex) { try { (resolveSchedulerBean(, , true)); } catch (NoSuchBeanDefinitionException ex2) { if (()) { ("More than one TaskScheduler bean exists within the context, and " + "none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " + "(possibly as an alias); or implement the SchedulingConfigurer interface and call " + "ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " + ()); } } } catch (NoSuchBeanDefinitionException ex) { // Search for ScheduledExecutorService bean next... try { (resolveSchedulerBean(, , false)); } catch (NoUniqueBeanDefinitionException ex2) { try { (resolveSchedulerBean(, , true)); } catch (NoSuchBeanDefinitionException ex3) { } } catch (NoSuchBeanDefinitionException ex2) { // Giving up -> falling back to default scheduler within the registrar... ("No TaskScheduler/ScheduledExecutorService bean found for scheduled processing"); } } } (); }
Here, we will first look for beans of type SchedulingConfigurer from beanfacotry, and then call configureTasks to load custom schedule configuration information. Here, the parameter is registerr, and then look for beans of type TaskScheduler and ScheduledExecutorService from the container to initialize taskScheduler. Finally, call(). There is another step to ensure the protection. If the schedule is still empty, a scheduler of ConcurrentTaskScheduler type is created by default.
()method
protected void scheduleTasks() { if ( == null) { = (); = new ConcurrentTaskScheduler(); } if ( != null) { for (TriggerTask task : ) { addScheduledTask(scheduleTriggerTask(task)); } } if ( != null) { for (CronTask task : ) { addScheduledTask(scheduleCronTask(task)); } } if ( != null) { for (IntervalTask task : ) { addScheduledTask(scheduleFixedRateTask(task)); } } if ( != null) { for (IntervalTask task : ) { addScheduledTask(scheduleFixedDelayTask(task)); } } }
Task execution
Let’s look back at the registration of the () task, here we look at the default () method.
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) { try { if () { return new EnterpriseConcurrentTriggerScheduler().schedule(decorateTask(task, true), trigger); } else { ErrorHandler errorHandler = ( != null ? : (true)); return new ReschedulingRunnable(task, trigger, , , errorHandler).schedule(); } } catch (RejectedExecutionException ex) { throw new TaskRejectedException("Executor [" + + "] did not accept task: " + task, ex); } }
Finally, call ReschedulingRunnable(task, trigger, , executor, errorHandler).schedule()
See the schedule method of ReschedulingRunnable
public ScheduledFuture<?> schedule() { synchronized () { = (); if ( == null) { return null; } long initialDelay = () - ().millis(); = (this, initialDelay, ); return this; } }
Calculate the next execution time initialDelay, and use the thread pool executor to delay execution of the current ReschedulingRunnable.
Run method
public void run() { Date actualExecutionTime = new Date(().millis()); (); Date completionTime = new Date(().millis()); synchronized () { ( != null, "No scheduled execution"); (, actualExecutionTime, completionTime); if (!obtainCurrentFuture().isCancelled()) { schedule(); } } }
Here () is to call the ReschedulingRunnable extends DelegatingErrorHandlingRunnable constructor to create the incoming task, which is the original schedule annotation method.
() is the run method of DelegatingErrorHandlingRunnable
public DelegatingErrorHandlingRunnable(Runnable delegate, ErrorHandler errorHandler) { (delegate, "Delegate must not be null"); (errorHandler, "ErrorHandler must not be null"); = delegate; = errorHandler; } public void run() { try { (); } catch (UndeclaredThrowableException ex) { (()); } catch (Throwable ex) { (ex); } }
Return to the run method of ReschedulingRunnable. After executing the proxy task, if the task is not cancelled, the schedule() method is called for the next task execution. This completes the periodic execution of the task.
How to dynamically control timing tasks?
If you want to cancel or modify a task execution cycle, what should you do at this time?
At this time, you can use the SchedulingConfigurer interface mentioned above, which will expose the ScheduledTaskRegistrar class instance. As can be seen from the above code analysis, all tasks are initialized through this class, and tasks can be added dynamically through this class. And the schedule() method returns a ScheduledFuture, which can cancel the task by calling the cancel method.
Summarize
The above is personal experience. I hope you can give you a reference and I hope you can support me more.