Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 621|回复: 0

spring boot.定时任务问题记录(TaskScheduler/ScheduledExecutorService异常)

[复制链接]
  • TA的每日心情
    奋斗
    3 天前
  • 签到天数: 789 天

    [LV.10]以坛为家III

    2049

    主题

    2107

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    722638
    发表于 2021-8-27 15:44:29 | 显示全部楼层 |阅读模式

    一、背景

    spring boot的定时任务非常简单,只需要在启动类中加上@EnableScheduling注解,然后在对应的方法上配置@Scheduled就可以了,系统会自动处理并按照Scheduled中的配置定时执行方法。

    但是在启动项目的时候,发生了很诡异的现象,有两个TaskScheduler/ScheduledExecutorService的异常打印了出来。但是系统并没有受影响,依然正常启动,而且定时任务也是正常执行。

    2018-09-29 15:54:05,187 DEBUG main org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor [//] Could not find default TaskScheduler bean
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.scheduling.TaskScheduler' available
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:996)
    	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.resolveSchedulerBean(ScheduledAnnotationBeanPostProcessor.java:278)
    	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:221)
    	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:200)
    	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:94)
    	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:167)
    	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
    	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:383)
    	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:337)
    	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:882)
    	at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:144)
    	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:545)
    	at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
    	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
    	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370)
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162)
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151)
    	at com.cmft.RunApp.main(RunApp.java:34)
    2018-09-29 15:54:05,190 DEBUG main org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor [//] Could not find default ScheduledExecutorService bean
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.concurrent.ScheduledExecutorService' available
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:996)
    	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.resolveSchedulerBean(ScheduledAnnotationBeanPostProcessor.java:278)
    	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:241)
    	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:200)
    	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:94)
    	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:167)
    	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
    	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:383)
    	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:337)
    	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:882)
    	at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:144)
    	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:545)
    	at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
    	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
    	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370)
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162)
    	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151)
    	at com.cmft.RunApp.main(RunApp.java:34)
    2018-09-29 15:54:05,190 INFO  main org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor [//] No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
    

    二、分析

    虽然没有影响系统,但是有异常看着总归很难受,于是尝试去探究下这个异常的原因。

    通过观察我们这两个异常是日志模块打印出来的debug级别日志

    2018-09-29 15:54:05,187 DEBUG main org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor [//] Could not find default TaskScheduler bean
    2018-09-29 15:54:05,190 DEBUG main org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor [//] Could not find default ScheduledExecutorService bean
    

    而且都是在ScheduledAnnotationBeanPostProcessor类中打印出来。看类的名字我们大概能猜到这是@Scheduled注解的处理类。

    根据日志内容我们定位到这个问题是由finishRegistration()方法打印出来。

    private void finishRegistration() {
            if (this.scheduler != null) {
                this.registrar.setScheduler(this.scheduler);
            }
    
            if (this.beanFactory instanceof ListableBeanFactory) {
                Map<String, SchedulingConfigurer> configurers = ((ListableBeanFactory)this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
                Iterator var2 = configurers.values().iterator();
    
                while(var2.hasNext()) {
                    SchedulingConfigurer configurer = (SchedulingConfigurer)var2.next();
                    configurer.configureTasks(this.registrar);
                }
            }
    
            if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
                Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type");
    
                try {
                    this.registrar.setTaskScheduler((TaskScheduler)this.resolveSchedulerBean(TaskScheduler.class, false));
                } catch (NoUniqueBeanDefinitionException var8) {
                    try {
                        this.registrar.setTaskScheduler((TaskScheduler)this.resolveSchedulerBean(TaskScheduler.class, true));
                    } catch (NoSuchBeanDefinitionException var7) {
                        if (this.logger.isInfoEnabled()) {
                            this.logger.info("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: " + var8.getBeanNamesFound());
                        }
                    }
                } catch (NoSuchBeanDefinitionException var9) {
                    this.logger.debug("Could not find default TaskScheduler bean", var9);
    
                    try {
                        this.registrar.setScheduler(this.resolveSchedulerBean(ScheduledExecutorService.class, false));
                    } catch (NoUniqueBeanDefinitionException var5) {
                        try {
                            this.registrar.setScheduler(this.resolveSchedulerBean(ScheduledExecutorService.class, true));
                        } catch (NoSuchBeanDefinitionException var4) {
                            if (this.logger.isInfoEnabled()) {
                                this.logger.info("More than one ScheduledExecutorService 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: " + var5.getBeanNamesFound());
                            }
                        }
                    } catch (NoSuchBeanDefinitionException var6) {
                        this.logger.debug("Could not find default ScheduledExecutorService bean", var6);
                        this.logger.info("No TaskScheduler/ScheduledExecutorService bean found for scheduled processing");
                    }
                }
            }
    
            this.registrar.afterPropertiesSet();
        }
    

    看代码我们会发现,spring会从先从注册过的bean中找任务调度器TaskScheduler

     this.registrar.setTaskScheduler((TaskScheduler)this.resolveSchedulerBean(TaskScheduler.class, false));
    

    如果获取不到会抛出异常,然后打印出来

    this.logger.debug("Could not find default TaskScheduler bean", var9);
    

    然后继续寻找定时任务的执行类ScheduledExecutorService

    this.registrar.setScheduler(this.resolveSchedulerBean(ScheduledExecutorService.class, false));
    

    如果找不到,会继续抛出异常并打印出来

    catch (NoSuchBeanDefinitionException var6) {
                        this.logger.debug("Could not find default ScheduledExecutorService bean", var6);
                        this.logger.info("No TaskScheduler/ScheduledExecutorService bean found for scheduled processing");
                    }
    

    如此,便看到开头我们看到的两个异常。

    看后面代码我们会发现,如果注册的bean中找不到,会调用scheduleTasks()方法初始化TaskScheduler,所以这两个异常并不会影响系统。可能spring 的开发者是想借此提醒开发人员要自己在系统中注册要使用的bean,而不是依赖框架来默认初始化。

    解决办法

    修改日志级别

    第一种是眼不见为净法,简单粗暴地修改日志级别就好。

    log4j.logger.org.springframework.scheduling = INFO
    

    这样debug级别的日志就不会被打印出来,只有最后的info级别日志才会被打印。

    注册TaskScheduler

    既然提示的异常是注册的bean中找不到TaskScheduler,那么我们就注册TaskScheduler。

        @Bean
        public TaskScheduler scheduledExecutorService() {
            ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
            scheduler.setPoolSize(8);
            scheduler.setThreadNamePrefix("scheduled-thread-");
            return scheduler;
        }
    
    

    阅读原文

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-9-9 13:45 , Processed in 4.188281 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表