一、背景

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;
}

阅读原文

spring boot.定时任务问题记录(TaskScheduler/ScheduledExecutorService异常)的更多相关文章

  1. Spring Boot定时任务应用实践

    在Spring Boot中实现定时任务功能,可以通过Spring自带的定时任务调度,也可以通过集成经典开源组件Quartz实现任务调度. 一.Spring定时器 1.cron表达式方式 使用自带的定时 ...

  2. Spring Boot 之日志记录

    Spring Boot 之日志记录 Spring Boot 支持集成 Java 世界主流的日志库. 如果对于 Java 日志库不熟悉,可以参考:细说 Java 主流日志工具库 关键词: log4j, ...

  3. Spring Boot 2 实践记录之 封装依赖及尽可能不创建静态方法以避免在 Service 和 Controller 的单元测试中使用 Powermock

    在前面的文章中(Spring Boot 2 实践记录之 Powermock 和 SpringBootTest)提到了使用 Powermock 结合 SpringBootTest.WebMvcTest ...

  4. Spring Boot 2 实践记录之 使用 ConfigurationProperties 注解将配置属性匹配至配置类的属性

    在 Spring Boot 2 实践记录之 条件装配 一文中,曾经使用 Condition 类的 ConditionContext 参数获取了配置文件中的配置属性.但那是因为 Spring 提供了将上 ...

  5. Spring Boot 2 实践记录之 MyBatis 集成的启动时警告信息问题

    按笔者 Spring Boot 2 实践记录之 MySQL + MyBatis 配置 中的方式,如果想正确运行,需要在 Mapper 类上添加 @Mapper 注解. 但是加入此注解之后,启动时会出现 ...

  6. Spring Boot 报错记录

    Spring Boot 报错记录 由于新建的项目没有配置数据库连接启动报错,可以通过取消自动数据源自动配置来解决 解决方案1: @SpringBootApplication(exclude = Dat ...

  7. Spring Boot 定时任务单线程和多线程

    Spring Boot 的定时任务: 第一种:把参数配置到.properties文件中: 代码: package com.accord.task; import java.text.SimpleDat ...

  8. (14)Spring Boot定时任务的使用【从零开始学Spring Boot】

    本文介绍在 Spring Boot 中如何使用定时任务,使用非常简单,就不做过多说明了. com.kfit.base.scheduling.SchedulingConfig: package com. ...

  9. Spring Boot (十一): Spring Boot 定时任务

    在实际的项目开发工作中,我们经常会遇到需要做一些定时任务的工作,那么,在 Spring Boot 中是如何实现的呢? 1. 添加依赖 在 pom.xml 文件中只需引入 spring-boot-sta ...

随机推荐

  1. Ruby Range类

    创建: 2017/09/30    

  2. SceneView 追踪选择目标

    在编辑器的Scene视图中追踪选择目标,调试动作用 SceneView这个类没有说明文档比较蛋疼 在update中调用SceneViewCameraFace2Target函数,编辑器的OnInspec ...

  3. [Swift通天遁地]八、媒体与动画-(13)CoreText框架实现图文混排

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  4. async 函数-----------------解决异步操作隧道的亮光

    之前也学过,只是没有学好,公司现在用的都是async函数 , 所以决定把它弄懂.最近看了看阮一峰的博客,做下记录. 异步I/O不就是读取一个文件吗,干嘛要搞得这么复杂?异步编程的最高境界,就是根本不用 ...

  5. Android json 数据解析

    1.json格式 2.json解析 3.gson解析 4.fastjson解析 一.Json格式 json一种轻量级的数据交换格式.在网络上传输交换数据一般用xml, json. 两种结构: 1)对象 ...

  6. Coursera公开课-Machine_learing:编程作业4

    编程作业: Neural Network Learning 源码上传到gitlab. 对于神经网络的理解也都在源码注释里面了,感兴趣可以看看.

  7. cocos2dx实现单机版三国杀(二)

    接上续,东西还没有做完 所以代码免不了改动 之前的头文件现在又改了不少,因为架构也改变了现在主要类就是GameScene.GameUI.PlayInfo.Poker这四个类  前面想的GameLoop ...

  8. MySQL 执行计划中Extra的浅薄理解

    1.using where: Extra中出现“Using where”,通常来说,意味着全表扫描或者在查找使用索引的情况下,但是还有查询条件不在索引字段当中. 如果需要回表也是用这个. 2.usin ...

  9. 00-SQLite的SQL语法

    SQLite的SQL语法 SQLite库可以解析大部分标准SQL语言.但它也省去了一些特性并且加入了一些自己的新特性.这篇文档就是试图描述那些SQLite支持/不支持的SQL语法的.查看关键字列表. ...

  10. graphite 绘图工具

    graphite 绘图工具