spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.18k stars 40.68k forks source link

WebSocket autoconfiguration issue with messageBrokerTaskScheduler in 3.3.0 version #40991

Closed bilak closed 5 months ago

bilak commented 5 months ago

Hello, after upgrade from 3.2.3 to 3.3.0 without any changes we have issue with websocket config.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'performanceMonitoringService' defined in file [/backbone/modules/base/spring-service/build/classes/java/main/com/eurofunk/eocs/monitoring/PerformanceMonitoringService.class]: Unsatisfied dependency expressed through constructor parameter 2: Error creating bean with name 'customStompRelayMessageBrokerConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0: Error creating bean with name 'org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration$WebSocketMessageConverterConfiguration': Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'messageBrokerTaskScheduler': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:237)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1357)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1194)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352)
    at myapp.core.deployment.EocsApplication.run(EocsApplication.java:55)
    at myapp.core.deployment.EocsApplication.run(EocsApplication.java:88)
    at myapp.Application.main(Application.java:157)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91)
    at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53)
    at org.springframework.boot.loader.launch.PropertiesLauncher.main(PropertiesLauncher.java:574)
    at myapp.BootstrapApplication.main(BootstrapApplication.java:68)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91)
    at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53)
    at org.springframework.boot.loader.launch.PropertiesLauncher.main(PropertiesLauncher.java:574)

the PerformanceMonitoringService has constructor like this

@Autowired(required = false)
public PerformanceMonitoringService(
    @Nullable final ServletWebServerApplicationContext servletWebServerApplicationContext,
    @Nullable @Qualifier("redisConnectionPools") final Map<
        Class,
        GenericObjectPool
    > redisConnectionPools,
    final List<ThreadPoolTaskScheduler> taskSchedulers,
    final List<ThreadPoolTaskExecutor> taskExecutors
) { ...

and we are overriding the DelegatingWebSocketMessageBrokerConfiguration with

@Override
@Bean(name = { "messageBrokerTaskScheduler", "messageBrokerSockJsTaskScheduler" })
public ThreadPoolTaskScheduler messageBrokerTaskScheduler() {
    final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
    scheduler.setThreadNamePrefix("MessageBrokerPool-");
    scheduler.setPoolSize(threadPoolSize);
    scheduler.setRemoveOnCancelPolicy(true);
    return scheduler;
}

when I remove that method application starts...however it's weird that this started to happen without any other change. I've also seen this issue which seems to be related however I don't use such method to set task scheduler.

wilkinsona commented 5 months ago

Thanks for the report but it's not clear why you believe this to be a Spring Boot bug, particularly when you've referenced a Spring Framework issue as being related. If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

bilak commented 5 months ago

@wilkinsona sorry didn't realised that it's framework's issue. I'm attaching the project which simulates the circular dependency. websocket-issue.zip

wilkinsona commented 5 months ago

The cycle is happening because you're sub-classing DelegatingWebSocketMessageBrokerConfiguration and, in the same @Configuration class, you're defining a bean that a WebSocketMessageBrokerConfigurer consumes. As the javadoc of DelegatingWebSocketMessageBrokerConfiguration suggests, this is atypical and you should use @EnableWebSocketMessageBroker instead. If you need to sub-class DelegatingWebSocketMessageBrokerConfiguration, you shouldn't define additional beans in the same class. As you have seen here, it's a brittle arrangement that will break as soon as you define a bean that's consumed by a WebSocketMessageBrokerConfigurer as is now the case due to https://github.com/spring-projects/spring-boot/pull/39611.