spring boot.定时任务问题记录(TaskSchedulerScheduledExecutorService异常)

mac2022-06-30  21

一、背景

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

阅读原文

转载于:https://www.cnblogs.com/MarsCheng/p/9734415.html

最新回复(0)