问题
I'm trying to implement a WebSocket server in Spring without STOMP, following this article: http://www.devglan.com/spring-boot/spring-websocket-integration-example-without-stomp
WebSocketConfig.java
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired private WebSocketHandler webSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/app");
}
}
WebSocketHandler
@Component
public class WebSocketHandler extends TextWebSocketHandler {
private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
// ...
}
}
However, I get this exception at startup:
java.lang.IllegalStateException: Unexpected use of scheduler.
at org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport$NoOpScheduler.schedule(WebSocketConfigurationSupport.java:108) ~[spring-websocket-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleCronTask(ScheduledTaskRegistrar.java:414) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.config.ScheduledTaskRegistrar.scheduleTasks(ScheduledTaskRegistrar.java:352) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.config.ScheduledTaskRegistrar.afterPropertiesSet(ScheduledTaskRegistrar.java:332) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:280) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:211) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:102) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:399) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:353) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:887) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161) ~[spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:552) ~[spring-context-5.0.4.RELEASE.jar:5.0.4.RELEASE]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234) [spring-boot-2.0.0.RELEASE.jar:2.0.0.RELEASE]
at my.package.App.main(App.java:31) [main/:na]
How to make this simple WebSocket app work?
回答1:
https://github.com/spring-projects/spring-boot/issues/12814
"@EnableWebSocket with no further configuration might create a no op scheduler which will conflict with any other use of a task scheduler."
This should be added to the config:
@Bean
public TaskScheduler taskScheduler() {
return new ConcurrentTaskScheduler(Executors.newSingleThreadScheduledExecutor());
}
回答2:
According your stack trace (ScheduledAnnotationBeanPostProcessor
) you have somewhere in the application @EnableScheduling
and that one tries to rely on the:
@Bean
public TaskScheduler defaultSockJsTaskScheduler() {
return initTaskScheduler();
}
private TaskScheduler initTaskScheduler() {
if (this.scheduler == null) {
ServletWebSocketHandlerRegistry registry = initHandlerRegistry();
if (registry.requiresTaskScheduler()) {
ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler();
threadPoolScheduler.setThreadNamePrefix("SockJS-");
threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors());
threadPoolScheduler.setRemoveOnCancelPolicy(true);
this.scheduler = threadPoolScheduler;
}
else {
this.scheduler = new NoOpScheduler();
}
}
return this.scheduler;
}
Where that NoOpScheduler
is accepted but causes problems at the runtime for your scheduled tasks.
I suggest to provide a SchedulingConfigurer
implementations and inject some TaskScheduler
bean into the ScheduledTaskRegistrar
. See @EnableScheduling
JavaDocs:
* <p>Implementing {@code SchedulingConfigurer} also allows for fine-grained
* control over task registration via the {@code ScheduledTaskRegistrar}.
* For example, the following configures the execution of a particular bean
* method per a custom {@code Trigger} implementation:
*
* <pre class="code">
* @Configuration
* @EnableScheduling
* public class AppConfig implements SchedulingConfigurer {
*
* @Override
* public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
* taskRegistrar.setScheduler(taskScheduler());
* taskRegistrar.addTriggerTask(
* new Runnable() {
* public void run() {
* myTask().work();
* }
* },
* new CustomTrigger()
* );
* }
*
* @Bean(destroyMethod="shutdown")
* public Executor taskScheduler() {
* return Executors.newScheduledThreadPool(42);
* }
*
* @Bean
* public MyTask myTask() {
* return new MyTask();
* }
* }</pre>
I found a couple JIRAs on the matter: https://jira.spring.io/browse/SPR-16189, https://jira.spring.io/browse/SPR-16705
来源:https://stackoverflow.com/questions/49742401/spring-websocket-simple-example-java-lang-illegalstateexception-unexpected-us