我在使用带@ServerEndPoint 注释类的 spring 时遇到问题

我正在使用 Springboot 1.2.3,我正在尝试弄清楚如何拥有端点的单个实例

@SpringBootApplication 
@EnableJpaRepositories 
@EnableWebSocket 
public class ApplicationServer { 
    public static void main(String[] args) { 
        SpringApplication.run(ApplicationServer.class, args); 
    } 
} 

Spring 配置:

@ConditionalOnWebApplication 
@Configuration 
public class WebSocketConfigurator { 
 
    @Bean 
    public ServerEndPoint serverEndpoint() { 
        return new ServerEndPoint(); 
    } 
 
    @Bean 
    public ServerEndpointExporter serverEndpointExporter() { 
        return new ServerEndpointExporter(); 
    } 
} 

WebSocket 端点:

@ServerEndpoint(value = "/", decoders = MessageDecoder.class,  
encoders = MessageEncoder.class, configurator = SpringConfigurator.class) 
public class ServerEndPoint { 
 
    private static final Logger LOG = LoggerFactory.getLogger(ServerEndPoint.class); 
 
    public static final Set<CommunicationObserver> OBSERVERS = Sets.newConcurrentHashSet(); 
 
    @OnMessage 
    public void onMessage(Session session, Message msg) { 
        LOG.debug("Received msg {} from {}", msg, session.getId()); 
        for (CommunicationObserver o : OBSERVERS) { 
            o.packetReceived(session, msg); 
        } 
    } 

这是基于 Spring WebSocket JSR-356 tutorial ,但我有以下错误:

java.lang.IllegalStateException: Failed to find the root WebApplicationContext. Was ContextLoaderListener not used? 
    at org.springframework.web.socket.server.standard.SpringConfigurator.getEndpointInstance(SpringConfigurator.java:68) 
    at org.apache.tomcat.websocket.pojo.PojoEndpointServer.onOpen(PojoEndpointServer.java:50) 
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.init(WsHttpUpgradeHandler.java:138) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:687) 
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223) 
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558) 
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
    at java.lang.Thread.run(Thread.java:745) 

我已经在嵌入式模式和外部 tomcat 8 和 jetty 9 下进行了测试(在外部模式下,我删除了 de Spring 配置文件)但是出现了同样的错误。

the only workaround i've found is to create a custom configurator.

public class SpringEndpointConfigurator extends ServerEndpointConfig.Configurator { 
 
    private static WebApplicationContext wac; 
 
    public SpringEndpointConfigurator() { 
    } 
 
    public SpringEndpointConfigurator(WebApplicationContext wac) { 
        SpringEndpointConfigurator.wac = wac; 
    } 
 
    @Override 
    public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException { 
        T endPoint = wac.getAutowireCapableBeanFactory().getBean(endpointClass); 
        return (endPoint != null) ? endPoint : wac.getAutowireCapableBeanFactory().createBean(endpointClass); 
    } 

它被创建为带有参数化构造函数的@Bean。

我一定是错过了一些用 SpringConfigurator 类完成它的东西,但我不知道是什么。

请您参考如下方法:

SpringConfigurator 使用ContextLoader 获取spring 上下文。 Spring Boot 确实设置了 ServletContext 但它从不使用 ContextLoaderListener 来初始化 ContextLoader 以保持 spring 上下文的静态状态。您可以尝试添加 ContextLoaderListener 或作为解决方法,您可以编写自己的上下文持有者和配置器。

这是一个例子:

第一个上下文持有者和配置器:

import org.springframework.beans.BeansException; 
import org.springframework.beans.factory.BeanFactory; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.ApplicationContextAware; 
 
import javax.websocket.server.ServerEndpointConfig; 
 
public class CustomSpringConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware { 
 
    /** 
     * Spring application context. 
     */ 
    private static volatile BeanFactory context; 
 
    @Override 
    public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException { 
        return context.getBean(clazz); 
    } 
 
    @Override 
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
        CustomSpringConfigurator.context = applicationContext; 
    } 
} 

要获取上下文,我们需要将其配置为 Bean:

@ConditionalOnWebApplication 
@Configuration 
public class WebSocketConfigurator { 
 
... 
 
    @Bean 
    public CustomSpringConfigurator customSpringConfigurator() { 
        return new CustomSpringConfigurator(); // This is just to get context 
    } 
} 

然后你需要正确设置配置器:

@ServerEndpoint(value = "/", decoders = MessageDecoder.class,  
encoders = MessageEncoder.class, configurator = CustomSpringConfigurator.class) 
public class ServerEndPoint { 
... 
} 

作为旁注,是的,如果您删除 SpringConfigurator,您的应用程序将启动并且您可以处理请求。但是你不能 Autowiring 其他 bean。


评论关闭
IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!