preface

Daily does not need all services are immediately need to return to the client response, some tasks need to be fixed time to execute, at this time we think of the use of timer, timer writing is very simple. But sometimes without understanding how this works, it is easy to have minor problems (such as their thread pool sharing).

The parsing process

A small demo is prepared locally. Inside there is a simple timer that is supposed to run every minute. And then you need to add the @enablesCheduling annotation to the startup class.

  1. Code section

    @Component
    public class TimeScheduled {
        @Scheduled(cron = "* 0/1 * * * ?" )
        public void test(a){
            System.out.println("yyy"); }}Copy the code
  2. The main attention is @ EnableScheduling annotations, after the introduction of a processor ScheduledAnnotationBeanPostProcessor inside. Here is the parse Bean encapsulating its task. Main methods:

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if(! (beaninstanceofAopInfrastructureBean) && ! (beaninstanceofTaskScheduler) && ! (beaninstanceof ScheduledExecutorService)) { // Not belonging to a component beanClass<? > targetClass = AopProxyUtils.ultimateTargetClass(bean);// Get the final bean (possibly proxied earlier)
            if (!this.nonAnnotatedClasses.contains(targetClass)) {
            // Get the Scheduled method collection
                Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, (method) -> {
                    Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);
                    return! scheduledMethods.isEmpty() ? scheduledMethods :null;
                });
                if (annotatedMethods.isEmpty()) {
                    this.nonAnnotatedClasses.add(targetClass);
                    if (this.logger.isTraceEnabled()) {
                        this.logger.trace("No @Scheduled annotations found on bean class: "+ targetClass); }}else {
                	Loop through the parse expression and register the scheduled task with ScheduledTaskRegistrar
                    annotatedMethods.forEach((method, scheduledMethods) -> {
                        scheduledMethods.forEach((scheduled) -> {
                            this.processScheduled(scheduled, method, bean);
                        });
                    });
                    if (this.logger.isTraceEnabled()) {
                        this.logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "'."+ annotatedMethods); }}}return bean;
        } else {
            return bean;
        }
    Copy the code
  3. Parsed will corresponding timing method according to the type of packaging to org. Springframework. Scheduling. Config. ScheduledTaskRegistrar, there are several kind of task set inside.

    private List<TriggerTask> triggerTasks; // The trigger task is not used this time
    private List<CronTask> cronTasks; // A task with a cron expression
    private List<IntervalTask> fixedRateTasks; // Tasks with fixedRate
    private List<IntervalTask> fixedDelayTasks; // Tasks with fixedDelay
    private final Map<Task, ScheduledTask> unresolvedTasks = new HashMap(16); // As a set of tasks, after adding the above tasks will be thrown into this set
    private final Set<ScheduledTask> scheduledTasks = new LinkedHashSet(16); // The set of tasks after execution
    Copy the code
  4. Adding tasks to a collection, when will the task be executed? We can see that the post-bean processor also implements the ApplicationListener interface. Then the ScheduledTaskRegistrar’s afterPropertiesSet method was used to invoke the corresponding execution process.

  5. afterScheduledExecutorServiceAccording toorg.springframework.scheduling.support.CronTrigger#nextExecutionTimeCalculate the next time to execute the task.

Q&A

  1. Multiple task timers cross time and find that some tasks are queued up to time and not executed (default thread pool configuration)

    While creating the taskScheduler Bean in the Refresh phase, Org. Springframework. Scheduling. Concurrent. ThreadPoolTaskScheduler inside the default number of threads to 1 size, once at the start of the task execution process in accordance with the thread pool, the rest of the task will be into the queue.

Added confusion

Looked at the timer inside the code, may not be deep enough cognition, this or some questions.

  1. Do you have any questions (I have some questions)? becauseScheduledTaskRegistrarClass implements theInitializingBeanInterface, so why not call it automatically when the container performs this step, and also use context events to listen and then make method calls?

    The ScheduledTaskRegistrar class is not a container Bean. The ScheduledTaskRegistrar class is not called via Spring startup resolution. If you beanize the class separately, then the Bean is inside the container, not associated with the timing, and will not be executed because there are no elements. And then parse InitializingBean and the inside of the execution method, the corresponding Bean has yet to be completed initialization (one is finishBeanFactoryInitialization steps, one is in finishRefresh execution). (In my opinion, I could have just dropped the InitializingBean implementation. After all, IT is not needed to make the class look more specialized.)

conclusion

This period of time to see a lot of source code, feeling that some things still need you to go to see, the world below is more wonderful, perhaps you can answer the interviewer and so on, but once asked, do you know how to achieve the bottom? Why? Let you design how to do? (All roads lead to Rome, this is my current road.)