精华内容
下载资源
问答
  • 生产问题分析之springboot定时任务莫名其妙停止了
    2022-05-12 17:04:28

    目录

            1.问题发现与描述

            2.springboot定时任务的注册

            3.springboot定时任务的调度与执行

            4.总结


    1.问题发现与描述

            生产上一个服务定时从上游系统拉取数据,突然任务停止了,不再继续执行了。查看日志发现报了异常,springboot的数据库连接池不够了。但是连接池不够为什么会影响到定时任务呢?下面是破案全过程。

            

    @Configuration
    @EnableScheduling
    public class DynamicScheduleAsyncTask implements SchedulingConfigurer {
    
        @Autowired
        private QueryRepository queryRepository;
        @Autowired
        private AsyncTimerTask timerTask;
    
        @Override
        public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
            taskRegistrar.addTriggerTask(timerTask,
                    triggerContext -> {
                        //2.1 从数据库获取执行周期
                        String cron = queryRepository.queryCron();
                        //2.3 返回执行周期(Date)
                        return new CronTrigger(cron).nextExecutionTime(triggerContext);
                    }
            );
        }
    }

         这是配置定时任务的代码,我们要支持动态调整cron周期,所以选择了triggerTask。这个是springboot定时任务的基本配置与使用。接下来我们跟踪源码解决问题。

    2.springboot定时任务的注册

            首先我们在配置类中加上了@EnableScheduling注解。这个注解向spring容器注入了一个定时任务的自动配置类。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(SchedulingConfiguration.class)
    @Documented
    public @interface EnableScheduling {
    
    }

            我们再来看看SchedulingConfiguration这个配置类做了什么。

    @Configuration(proxyBeanMethods = false)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public class SchedulingConfiguration {
    
    	@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    	public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
    		return new ScheduledAnnotationBeanPostProcessor();
    	}
    
    }

            很明显这个配置类向spring容器中注入了一个后置处理器ScheduledAnnotationBeanPostProcessor。所谓后置处理器就是指spring在初始化的过程中会回调这些类的某些方法。接下来走进这个后置处理器的postProcessAfterInitialization方法。

    if (!this.nonAnnotatedClasses.contains(targetClass) &&
    				AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) {
                //找到所有标记了@Scheduled注解的方法
    			Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
    					(MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
    						Set<Scheduled> scheduledAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(
    								method, Scheduled.class, Schedules.class);
    						return (!scheduledAnnotations.isEmpty() ? scheduledAnnotations : null);
    					});
    			if (annotatedMethods.isEmpty()) {
    				this.nonAnnotatedClasses.add(targetClass);
    				if (logger.isTraceEnabled()) {
    					logger.trace("No @Scheduled annotations found on bean class: " + targetClass);
    				}
    			}
    			else {
    				// Non-empty set of methods
                    //processScheduled这个方法中将当前任务放进集合
    				annotatedMethods.forEach((method, scheduledAnnotations) ->
    						scheduledAnnotations.forEach(scheduled -> 
    processScheduled(scheduled, method, bean)));
    				if (logger.isTraceEnabled()) {
    					logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
    							"': " + annotatedMethods);
    				}
    			}
    		}

    3.springboot定时任务的调度与执行

            上边提到的ScheduledAnnotationBeanPostProcessor这个后置处理器还有一个作用,它实现了ApplicationListener,顾名思义它还是个监听器,spring启动完成后回调所有监听器的onApplicationEvent方法,也正是在这个方法中完成了任务的调度与执行。

    @Override
    	public void onApplicationEvent(ContextRefreshedEvent event) {
    		if (event.getApplicationContext() == this.applicationContext) {
    			// Running in an ApplicationContext -> register tasks this late...
    			// giving other ContextRefreshedEvent listeners a chance to perform
    			// their work at the same time (e.g. Spring Batch's job registration).
    			finishRegistration();
    		}
    	}
    private void finishRegistration() {
    	this.registrar.afterPropertiesSet();
    }

    this.registrar默认是ScheduledTaskRegistrar。我们接着往下看:

    @Override
    public void afterPropertiesSet() {
    	scheduleTasks();
    }
    protected void scheduleTasks() {
    	//创建任务执行器 默认是ConcurrentTaskScheduler
    	if (this.taskScheduler == null) {
    		this.localExecutor = Executors.newSingleThreadScheduledExecutor();
    		this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
    	}
    	//triggerTasks不为空 我们直接看scheduleTriggerTask方法
    	if (this.triggerTasks != null) {
    		for (TriggerTask task : this.triggerTasks) {
    			addScheduledTask(scheduleTriggerTask(task));
    		}
    	}
    	if (this.cronTasks != null) {
    		for (CronTask task : this.cronTasks) {
    			addScheduledTask(scheduleCronTask(task));
    		}
    	}
    	if (this.fixedRateTasks != null) {
    		for (IntervalTask task : this.fixedRateTasks) {
    			addScheduledTask(scheduleFixedRateTask(task));
    		}
    	}
    	if (this.fixedDelayTasks != null) {
    		for (IntervalTask task : this.fixedDelayTasks) {
    			addScheduledTask(scheduleFixedDelayTask(task));
    		}
    	}
    }
    @Nullable
    public ScheduledTask scheduleTriggerTask(TriggerTask task) {
    	ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);
    	boolean newTask = false;
    	//scheduledTask为null new一个
    	if (scheduledTask == null) {
    		scheduledTask = new ScheduledTask(task);
    		newTask = true;
    	}
    	//taskScheduler默认实现是ConcurrentTaskScheduler,我们接着看它里边的schedule方法
    	if (this.taskScheduler != null) {
    		scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());
    	}
    	else {
    		addTriggerTask(task);
    		this.unresolvedTasks.put(task, scheduledTask);
    	}
    	return (newTask ? scheduledTask : null);
    }
    @Override
    @Nullable
    public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
    	try {
    		if (this.enterpriseConcurrentScheduler) {
    			return new EnterpriseConcurrentTriggerScheduler().schedule(decorateTask(task, true), trigger);
    		}
    		else {
    			//进入到这个分支 我们直接看ReschedulingRunnable里边的schedule方法
    			ErrorHandler errorHandler =
    					(this.errorHandler != null ? this.errorHandler : TaskUtils.getDefaultErrorHandler(true));
    			return new ReschedulingRunnable(task, trigger, this.clock, this.scheduledExecutor, errorHandler).schedule();
    		}
    	}
    	catch (RejectedExecutionException ex) {
    		throw new TaskRejectedException("Executor [" + this.scheduledExecutor + "] did not accept task: " + task, ex);
    	}
    }

    @Nullable
    public ScheduledFuture<?> schedule() {
    	synchronized (this.triggerContextMonitor) {
    		//回调我们的获取cron表达式的方法 计算下次执行时间
    		this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);
    		if (this.scheduledExecutionTime == null) {
    			return null;
    		}
    		//计算出距离下次执行的毫秒值
    		long initialDelay = this.scheduledExecutionTime.getTime() - this.triggerContext.getClock().millis();
    		//this.executor是JDK的延时线程池DelegatedScheduledExecutorService
    		//重要一点:第一个入参就是要被执行的任务,也就是当前对象ReschedulingRunnable
    		//所以延时时间到了之后,JDK线程池就会调用当前对象的run方法。
    		this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);
    		return this;
    	}
    }
    @Override
    public void run() {
    	Date actualExecutionTime = new Date(this.triggerContext.getClock().millis());
    	//这里就是回调了我们自定义的任务方法
    	super.run();
    	Date completionTime = new Date(this.triggerContext.getClock().millis());
    	synchronized (this.triggerContextMonitor) {
    		Assert.state(this.scheduledExecutionTime != null, "No scheduled execution");
    		//上述过程都是计算和更新下次执行任务的时间 虽然重要但是不是本次分析重点
    		this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);
    		if (!obtainCurrentFuture().isCancelled()) {
    			//重点在这儿:这里又调用了这个schedule方法
    			//那么代码又回到了上边schedule方法中。
    			schedule();
    		}
    	}
    }

    4.总结

            总结一下:其实道理很简单,所有的问题都出现在ReschedulingRunnable这个类中。

            1.schedule方法中从数据库获取cron表达式计算延时时间,然后将自己作为任务对象放到JDK的延时线程池中。

            2.JDK延时线程池执行任务时调用当前对象的run方法。

            3.当前对象的run方法再次计算延时时间,调用schedule方法。

            很明显,上述三个步骤形成了一个循环,正常情况下我们的任务就会一次次的执行下去直到JVM退出。但是文章开头提到,我们的数据库连接池不够了,在第一步从数据库获取cron表达式的时候抛出了异常,导致schedule方法执行到第一行就结束了。也就是说没有将任务放到JDK的线程池中,所以后续的run方法也就不会执行了。这个循环也就结束了。

            截止到目前我们已经将问题的原委全部弄通,怎们解决这个问题合适呢?

            第一我们调整了hikari连接池的参数,核心连接数由默认10调到了30.

            第二我们调整了hikari连接的超时时间,由默认的3000ms调整到了6000ms.

            第三我们在获取cron表到式的时候通过try catch将异常捕获,如果数据库异常我们给个默认的cron即可.

    更多相关内容
  • springboot 定时任务

    2019-02-24 11:27:03
    任务调度框架“Quartz”是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统
  • SpringBoot实现定时任务的动态开启关闭,如有疑问联系wx: hsj179540
  • SpringBoot定时任务实现Oracle和mysql数据同步
  • 使用springboot 构建的 spring task 定时任务,采用异步任务形式,防止任务堵塞.
  • springboot中通过读取数据库的定时任务信息,动态生成quartz定时任务
  • springboot定时任务demo 使用SpringBoot创建定时任务非常简单, 目前主要有以下三种创建方式: 一、基于注解(@Scheduled) 二、基于接口(SchedulingConfigurer) 前者相信大家都很熟悉,但是实际使用中我们往往想从...
  • https://blog.stormma.me/2017/05/08/springboot定时任务踩坑记录/ 前言 springboot已经支持了定时任务Schedule模块,一般情况已经完全能够满足我们的实际需求。今天就记录一下我使用 schedule 时候踩的坑吧。 想要...
  • springboot 定时任务(线程配置,并行【同步】、异步等)
  • SpringBoot定时任务

    2018-09-14 00:08:36
    SpringBoot定时任务,解析 fixedRate,fixedDelay, initialDelay的详细功能
  • 主要介绍了springboot 定时任务@Scheduled实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 主要介绍了SpringBoot 定时任务遇到的坑,今天踩的这个坑和 cron 表达式有关,文中给大家介绍了cron 表达式的解释,需要的朋友一起看看吧
  • springboot定时任务

    2022-02-21 10:20:47
    在平常的开发中很多时候我们都有用到定时任务,而springboot也为我们提供了注解式定时任务 1.启动类 @EnableScheduling //开启定时任务扫描 让springboot找到有定时任务注解的方法 @SpringBootApplication public ...

    在平常的开发中很多时候我们都有用到定时任务,而springboot也为我们提供了注解式定时任务

    1.启动类

    @EnableScheduling  //开启定时任务扫描 让springboot找到有定时任务注解的方法
    @SpringBootApplication
    public class ATestDemo1Application {
        public static void main(String[] args) {
            SpringApplication.run(ATestDemo1Application.class, args);
        }
    }

    2.定时任务

    @Service
    public class TestService {
        @Scheduled(fixedRate = 2000)   //2秒执行一次
        public void testScheduler01(){
            System.out.println("testScheduler01----->"+Thread.currentThread().getName());
        }
    
        @Scheduled(fixedRate = 2000)
        public void testScheduler02(){
            System.out.println("testScheduler02----->"+Thread.currentThread().getName());
        }
    }

    在这里我们的定时任务就已经准备完成了,启动程序后会按照指定规则执行方法,在这里我们打印两个方法说执行的线程名,会发现是同一个线程,springboot定时任务默认的是单线程执行,在上一个任务没有完成的情况下会阻塞下一个任务。

    3.实现多线程定时任务

    两种实现方法

    3.1创建一个TaskScheduler线程池并交给容器管理

    @Configuration
    public class ScheduleConfig  {
        @Bean
        public TaskScheduler taskScheduler(){
            ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
            //设置并发为10
            scheduler.setPoolSize(10);
            return scheduler;
        }
    }

    3.2实现SchedulingConfigurer接口,重写configureTasks方法给它set进一个线程池

    @Configuration
    public class ScheduleConfig implements SchedulingConfigurer {
        @Override
        public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
            //设定一个长度10的定时任务线程池
            scheduledTaskRegistrar.setScheduler(new ScheduledThreadPoolExecutor(10));
        }
    }

    两种方法选取一个即可

    4.@Scheduled中的参数说明:

    @Scheduled(fixedRate=2000):上一次开始执行时间点后2秒再次执行;
     
    @Scheduled(fixedDelay=2000):上一次执行完毕时间点后2秒再次执行;
     
    @Scheduled(initialDelay=1000, fixedDelay=2000):第一次延迟1秒执行,然后在上一次执行完毕时间点后2秒再次执行;
    
    @Scheduled(cron="* * * * * ?"):按cron规则执行。
    
    展开全文
  • springboot定时任务案例

    2022-02-16 23:38:03
    springboot定时任务案例1.通过@Scheduled注解配置定时任务1.pom.xml2.主程序开启定时任务功能3.测试2.通过Quartz配置定时任务1.pom.xml2.job13.job24.quartz配置 1.通过@Scheduled注解配置定时任务 1.pom.xml <?...

    1.通过@Scheduled注解配置定时任务

    1.pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.3</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>demo</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>11</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    

    2.主程序开启定时任务功能

    package com.example.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableScheduling;
    
    @SpringBootApplication
    @EnableScheduling //开启定时任务功能
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
    }
    
    

    3.测试

    package com.example.demo;
    
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    @Component
    public class MySchedule {
    
        //当前方法执行结束之后,下一秒,任务再次开启
        @Scheduled(fixedDelay = 1000)
        public void fixedDelay() {
            System.out.println("fixedDelay()..." + new Date());
        }
    
        //当前方法开始执行后1秒,任务再次开启
        @Scheduled(fixedRate = 1000)
        public void fixedRate() {
            System.out.println("fixedRate()..." + new Date());
        }
    
        //当前方法开始执行后1秒,任务再次开启
        @Scheduled(initialDelay = 1000,fixedRate = 1000)
        public void initDelay() {
            System.out.println("initDelay()..." + new Date());
        }
    
        //cron表达式,如下代表每5秒执行一次
        @Scheduled(cron = "0/5 * * * * *")
        public void cron() {
            System.out.println("cron()..." + new Date());
        }
    }
    
    

    2.通过Quartz配置定时任务

    1.pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.3</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.yl</groupId>
        <artifactId>quartz</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>quartz</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>11</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-quartz</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    

    2.job1

    package com.yl.quartz.job;
    
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    @Component
    public class MyJob1 {
        public void hello() {
            System.out.println("MyJob1()..." + new Date());
        }
    }
    
    

    3.job2

    package com.yl.quartz.job;
    
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    
    import java.util.Date;
    
    public class MyJob2 extends QuartzJobBean {
        private String name;
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            System.out.println("MyJob2()..." + name + new Date());
        }
    }
    
    

    4.quartz配置

    package com.yl.quartz.trigger;
    
    import com.yl.quartz.job.MyJob2;
    import org.quartz.JobDataMap;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.quartz.*;
    
    // quartz配置
    @Configuration
    public class QuartzConfig {
        //绑定job
        @Bean
        MethodInvokingJobDetailFactoryBean jobDetail1() {
            MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean();
            bean.setTargetBeanName("myJob1");
            bean.setTargetMethod("hello");
            return bean;
        }
    
        //绑定job
        @Bean
        JobDetailFactoryBean jobDetail2() {
            JobDetailFactoryBean bean = new JobDetailFactoryBean();
            bean.setJobClass(MyJob2.class);
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("name","root");
            bean.setJobDataMap(jobDataMap);
            return bean;
        }
    
        // 配置简单触发器
        @Bean
        SimpleTriggerFactoryBean simpleTriggerFactoryBean() {
            SimpleTriggerFactoryBean bean = new SimpleTriggerFactoryBean();
            //绑定jobDetail
            bean.setJobDetail(jobDetail1().getObject());
            //重复次数
            bean.setRepeatCount(5);
            //开始延迟时间
            bean.setStartDelay(1000);
            //间隔时间
            bean.setRepeatInterval(1000);
            return bean;
        }
    
        // 配置cron表达式触发器
        @Bean
        CronTriggerFactoryBean cronTriggerFactoryBean() {
            CronTriggerFactoryBean bean = new CronTriggerFactoryBean();
            bean.setJobDetail(jobDetail2().getObject());
            bean.setCronExpression("0/7 * * * * ?");
            return bean;
        }
    
        //配置启动器
        @Bean
        SchedulerFactoryBean schedulerFactoryBean() {
            SchedulerFactoryBean bean = new SchedulerFactoryBean();
            bean.setTriggers(simpleTriggerFactoryBean().getObject(),cronTriggerFactoryBean().getObject());
            return bean;
        }
    }
    
    
    展开全文
  • SpringBoot定时任务视频教程
  • 本篇文章主要介绍了SpringBoot 创建定时任务(配合数据库动态执行),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • (https://mp.weixin.qq.com/s/7deN_VIp3f9k1fRXsZS4NQ),其实实现这个需求的前提是你要搞明白 定时任务 的实现原理,这样你才有可能实现定时任务的动态增删启停,所以下面从源码的角度跟 SpringBoot定时任务原理。...

    写作目的

    最近看了一篇博客 “Spring Boot实现定时任务的动态增删启停” ,其实实现这个需求的前提是你要搞明白 定时任务 的实现原理,这样你才有可能实现定时任务的动态增删启停,所以下面从源码的角度跟 SpringBoot定时任务原理。

    代码下载

    https://gitee.com/cbeann/Demooo/tree/master/springboot-demo/src/main/java/com/example/scheduledemo

    源码分析

    引入@EnableScheduling注解做了什么?

    我们打开@EnableScheduling注解源码后发现

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(SchedulingConfiguration.class)
    @Documented
    public @interface EnableScheduling {
    
    }
    

    其实改注解同Import引入了一个类SchedulingConfiguration类,该类向IOC容器中注入了一个BeanPostProcessor:ScheduledAnnotationBeanPostProcessor

    @Configuration
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public class SchedulingConfiguration {
    
    	@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    	public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
    		return new ScheduledAnnotationBeanPostProcessor();
    	}
    
    }
    

    总结:@EnableScheduling 该类向IOC容器中注入了一个BeanPostProcessor:ScheduledAnnotationBeanPostProcessor

    ScheduledAnnotationBeanPostProcessor初始化做了什么?

    public ScheduledAnnotationBeanPostProcessor() {
    		this.registrar = new ScheduledTaskRegistrar();
    	}
    

    总结:就如代码所示,new一个ScheduledTaskRegistrar

    TaskComponent(自定义类)生命周期中ScheduledAnnotationBeanPostProcessor做了什么?

    下图是ScheduledAnnotationBeanPostProcessor.postProcessAfterInitialization方法的源码,第一个方框就是把带有Scheduled注解的方法找出来,第二个方框是对每一个方法执行processScheduled逻辑。

    在这里插入图片描述
    下面截取ScheduledAnnotationBeanPostProcessor.processScheduled方法里的部分代码,它会把方法放到ScheduledTaskRegistrar里(ScheduledAnnotationBeanPostProcessor无参构造方法new的对象)
    在这里插入图片描述

    什么时候开始启动定时任务呢?

    我们先详细看一下ScheduledAnnotationBeanPostProcessor的实现的接口和类,其实它实现了ApplicationListener,其实ScheduledAnnotationBeanPostProcessor也是一个监听器

    public class ScheduledAnnotationBeanPostProcessor
    		implements ScheduledTaskHolder, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor,
    		Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware,
    		SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean 
    

    那就有点门路了,最后肯定会调用ApplicationListener实现类(ScheduledAnnotationBeanPostProcessor)的某些方法,
    在这里插入图片描述
    最后其实跟下去,你就会发现他调用了ScheduledAnnotationBeanPostProcessor里ScheduledTaskRegistrar的scheduleTasks方法

    protected void scheduleTasks() {
    		if (this.taskScheduler == null) {
    			this.localExecutor = Executors.newSingleThreadScheduledExecutor();
    			this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
    		}
    		if (this.triggerTasks != null) {
    			for (TriggerTask task : this.triggerTasks) {
    				addScheduledTask(scheduleTriggerTask(task));
    			}
    		}
    		if (this.cronTasks != null) {
    			for (CronTask task : this.cronTasks) {
    				addScheduledTask(scheduleCronTask(task));
    			}
    		}
    		if (this.fixedRateTasks != null) {
    			for (IntervalTask task : this.fixedRateTasks) {
    				addScheduledTask(scheduleFixedRateTask(task));
    			}
    		}
    		if (this.fixedDelayTasks != null) {
    			for (IntervalTask task : this.fixedDelayTasks) {
    				addScheduledTask(scheduleFixedDelayTask(task));
    			}
    		}
    	}
    

    然后就开始执行了

    总结

    思路

    通过@EnableScheduling注解添加一个ScheduledAnnotationBeanPostProcessor,在Bean生命周期里拦截方法看是否有@Scheduled注解,如果有,把方法存到ScheduledAnnotationBeanPostProcessor里的ScheduledTaskRegistrar里。同时ScheduledAnnotationBeanPostProcessor又是linstener,最后ioc容器加载完毕后会通知linstener,然后就调用ScheduledAnnotationBeanPostProcessor里ScheduledTaskRegistrar的里存的方法去按照一定的逻辑去执行,就实现了以上逻辑。

    总结

    其实SpringBoot的自动装配原理也查不多,通过注解引入某个类(大多包含BeanPostProcessor),然后根据注解拦截Bean生命周期,把方法放在某个地方,然后最后通过监听器或者Runner去开启某个新功能。

    参考

    https://mp.weixin.qq.com/s/7deN_VIp3f9k1fRXsZS4NQ

    展开全文
  • 主要介绍了SpringBoot执行定时任务@Scheduled的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • 问题描述:程序中有一个定时任务,每半个小时执行一次,按照规则生成假数据插入到数据库中,突然有一天查看数据库,每次都会有两条数据,数据还不一样,意识到是不是定时任务重复执行了,就去看了log日志,发现定时...
  • 今天再写一个定时任务时,发现所有配置都没问题,但定时任务就是不执行,通过各种对比测试排除法,终于找到问题所在。下面看一下启动类 @SpringBootApplication @ComponentScan(value = "com.timer.timer_demo",...
  • @SpringBootApplication @EnableScheduling public class ManagerApplication { public static void main(String[] args) { SpringApplication.run(PwManagerApplication.class, args); } } 2.在方法上加上@...
  • 定时任务对于大多数需求来说,@注解级别就可以了,,,但是有些需要提前用数据库配置的,,也可以通过实现 SchedulingConfigurer 这个也是可以的,配置好后重启即可;还有一些情况是需要根据业务增删改查来动态生成...
  • java springboot 定时任务

    2022-01-19 14:01:05
    TaskScheduler 任务调度者 TaskerExecutor 任务执行者 @EnableScheduling //开启定时功能注解 @Scheduled(cron=“表达式”) //执行时间 参数为Cron表达式 启动类上添加开启定时功能 @EnableScheduling 编写逻辑...
  • 我们在平常项目开发中,经常会用到周期性定时任务,这个时候使用定时任务就能很方便的实现。在SpringBoot中用得最多的就是Schedule。
  • 首先定义的是异步线程池,需要再启动类添加注解 @EnableScheduling ...import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableA
  • 加上@EnableScheduling开启定时任务: @SpringBootApplication @EnableScheduling //开启定时任务 public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication...
  • 关于定时任务的详细博客。 1. 通过 @Scheduled 注解配置定时任务SpringBoot项目中使用 @Scheduled 注解只需要添加 Spring Web 依赖,并且在项目启动类中开启 @EnableScheduling 注解即可。 创建项目 在项目...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 32,232
精华内容 12,892
关键字:

springboot定时任务

spring 订阅
友情链接: 1.rar