I want to use spring-boot-starter-quartz and spring-boot-starter-websocket simultaneously,but it fails to satisfy my needs, and when I try to use them,some errors as follow appeared.
2020-12-08 18:50:09.535 [main] ERROR org.springframework.boot.SpringApplication [line:837] - Application run failed
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'defaultSockJsTaskScheduler' is expected to be of type 'org.springframework.scheduling.TaskScheduler' but was actually of type 'org.springframework.beans.factory.support.NullBean'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:399)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:227)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1175)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1142)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.resolveSchedulerBean(ScheduledAnnotationBeanPostProcessor.java:327)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:256)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:233)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:105)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:404)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:361)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:898)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:554)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
I have figured out what caused these errors. It is because that there is a logical conflict on the instantiation of Taskscheuler after the procedure of use spring-boot-starter-quartz and spring-boot-starter-websocket simultaneously. spring-wesocket created a NullBean as instantiation of Taskscheduler. when the quartz is applied,the errors would emerge that is: In org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport#defaultSockJsTaskScheduler
org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport#defaultSockJsTaskScheduler
@Bean
@Nullable
public TaskScheduler defaultSockJsTaskScheduler() {
if (initHandlerRegistry().requiresTaskScheduler()) {
ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler();
threadPoolScheduler.setThreadNamePrefix("SockJS-");
threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors());
threadPoolScheduler.setRemoveOnCancelPolicy(true);
this.scheduler = threadPoolScheduler;
}
return this.scheduler;
}
If this condition is not satisfied, it's going to create an instance(NullBean(org.springframework.beans.factory.support.NullBean)) of TaskScheduler here. So we're going to rely on spring-boot-starter-quartz:
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration#taskScheduler
@Bean
@ConditionalOnBean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@ConditionalOnMissingBean({ SchedulingConfigurer.class, TaskScheduler.class, ScheduledExecutorService.class })
public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
return builder.build();
}
But due to the existence of the Nullbean the above method is impracticable, neither for the creation of a new workable Taskscheduler, but this problem can be tackled in this way:
@Bean
public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
return builder.build();
}
Or implement WebSocketConfigurer to solve it. Although we can use the above ways to settle the errors, but they are only temporary .In my opinion,the root cause for the problem is the bug of spring-websocket and so that cause the wrong existence of Nullbean I think we can use these ways to deal with: - Change logical order of the two procedures, that is to put the spring-boot-starter-quartz before the instantiation - In spring-websocket, conditionally include TaskScheduler - Always create a TaskScheduler Bean without if{} - In spring-websocket, org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport#defaultSockJsTaskScheduler no longer as a Spring Bean
The above mentioned are my immature thoughts ,may be you can solve the errors in a better way,thanks for your attention
Comment From: rstoyanchev
Previously we had a no-op scheduler and changed it in https://github.com/spring-projects/spring-framework/issues/20737 so that now it return null
. This makes it look like there is none declared for the purpose of autowiring but here it affects @ConditionalOnMissingBean
. Always declaring it is guaranteed to cause issues by conflicting with other declarations. I just don't see anything we can do on the WebSocketConfigurationSupport
side since there is no way to create it conditionally at the Spring Framework level, which is what it's trying to do.
Could anything be done on the Boot side for this, for example to improve how ConditionalOnMissingBean
works for this scenario where a null was returned for a bean?
Comment From: snicoll
@felixu1992 thanks for the report. Could you please share a small sample that demonstrates the problem you've described? You can do so by attaching a zip to this issue or sharing a link to a GitHub repository.
Comment From: felixu1992
@snicoll Thank you for your reply,this is a small sample. You can get it form https://oss-blog.felixu.top/demo.zip
Comment From: snicoll
Thanks for the minimal sample @felixu1992
I think this is could be a framework problem after all. With a default @Scheduled
setup, we're going to detect the TaskScheduler
by type. While that NullBean
is not meant to be injected by type, org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveNamedBean
will still pick it up for some reason and fail as indicated in the exception above @jhoeller any idea about that?
@ConditionalOnMissingBean
uses beanFactory.getBeanNamesForType
and it does back off indeed due to the presence of that NullBean
:
TaskSchedulingAutoConfiguration#taskScheduler:
Did not match:
- @ConditionalOnMissingBean (types: org.springframework.scheduling.annotation.SchedulingConfigurer,org.springframework.scheduling.TaskScheduler,java.util.concurrent.ScheduledExecutorService; SearchStrategy: all) found beans of type 'org.springframework.scheduling.TaskScheduler' defaultSockJsTaskScheduler (OnBeanCondition)
Comment From: felixu1992
@snicoll Thank you for your attention,and feel glad to share thoughts with you.
Comment From: snicoll
After a discussion with Juergen, we concluded that something should be done at the framework level. I've created https://github.com/spring-projects/spring-framework/issues/26271
Comment From: felixu1992
After a discussion with Juergen, we concluded that something should be done at the framework level. I've created spring-projects/spring-framework#26271
I think so, too, so I started this issue on the Spring Framework, but it was transferred to Spring Boot.
Comment From: snicoll
This issue is now superseded by https://github.com/spring-projects/spring-framework/issues/26271. I've tried the fix and it works as expected now.
@felixu1992 you can also give it a try using Spring Framework 5.3.3-SNAPSHOT
. The fix will be included in the next release (2.4.2
).