28

我需要ServletContext从 a@ServerEndpoint中获取,以便找到 SpringApplicationContext并查找 Bean。

目前我最好的方法是将该 bean 绑定到 JNDI 命名上下文中并在Endpoint. 欢迎任何更好的解决方案。

我也在寻找一种合理的方式来同步 servletHttpSession和 websocket 的Session.

4

4 回答 4

43

servletHttpSession在 JSR-356HandshakeRequest#getHttpSession()中可用,当@OnOpen@ServerEndpoint. 反过来ServletContext只能通过HttpSession#getServletContext(). 那是两只鸟,一块石头。

为了捕获握手请求,实现一个ServerEndpointConfig.Configurator并覆盖该modifyHandshake()方法。在HandshakeRequest这里可以作为方法参数使用。你可以把它HttpSession放进去EndpointConfig#getUserProperties()。反过来EndpointConfig可用作方法参数@OnOpen

这是ServerEndpointConfig.Configurator实现的启动示例:

public class ServletAwareConfig extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        config.getUserProperties().put("httpSession", httpSession);
    }

}

以下是您可以使用它的方法,请注意 的configurator属性@ServerEndpoint

@ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {

    private EndpointConfig config;

    @OnOpen
    public void onOpen(Session websocketSession, EndpointConfig config) {
        this.config = config;
    }

    @OnMessage
    public void onMessage(String message) {
        HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
        ServletContext servletContext = httpSession.getServletContext();
        // ...
    }

}

作为设计提示,最好@ServerEndpoint完全摆脱 servlet API 依赖项。您最好在modifyHandshake()实现中立即准确地从 servlet 会话或上下文中提取所需信息(通常是可变的 Javabean),然后将它们放入用户属性映射中。如果你不这样做,那么你应该记住,websocket 会话可以比 HTTP 会话存活得更长。因此,当您仍然携带HttpSession到端点时,您可能会IllegalStateException在它过期时尝试访问它。

如果您碰巧手头有 CDI(可能还有 JSF),您可能会从OmniFaces<o:socket>的源代码中获得灵感(链接在展示的最底部)。

也可以看看:

于 2014-05-01T10:19:59.067 回答
5

更新了 BalusC 答案的代码,onOpen 方法需要用@OnOpen 修饰。那么就不需要再扩展 Endpoint 类了:

@ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {

    private EndpointConfig config;

    @OnOpen
    public void onOpen(Session websocketSession, EndpointConfig config) {
        this.config = config;
    }

    @OnMessage
    public void onMessage(String message) {
        HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
        ServletContext servletContext = httpSession.getServletContext();
        // ...
    }

}
于 2014-05-18T12:10:41.263 回答
1

我在 Tomcat(版本 7.0.56 和 8.0.14)上试用了 BalusC 的答案。在这两个容器上, modifyHandshake 的请求参数不包含 HttpSession(因此不包含 servletContext)。由于我只需要 servlet 上下文来访问“全局”变量(即 Web 应用程序全局变量),因此我只是将这些变量存储在持有者类的普通静态字段中。这是不优雅的,但它工作。

这看起来像是这个特定 tomcat 版本中的一个错误 - 有没有人也看到过这个?

于 2014-11-21T08:50:42.607 回答
0

有时我们无法session使用上面ServletAwareConfig的 BalusC,这是因为会话仍未创建。由于我们不是在寻找会话而是寻找 servletContext,所以在 tomcat 中我们可以这样做:

public static class ServletAwareConfig extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        try {
            Field reqfld = request.getClass().getDeclaredField("request");
            reqfld.setAccessible(true);
            HttpServletRequest req = (HttpServletRequest) reqfld.get(request);
            ServletContext ctxt = req.getServletContext();
            Map<String, Object> up = config.getUserProperties();
            up.put("servletContext", ctxt);
        } catch (NoSuchFieldException e) {
        } catch (SecurityException e) {
        } catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        }
    }

}

如果我们想立即初始化会话,我们可以调用 request.getSession()。

参考:Websocket - httpSession 返回 null

于 2018-11-29T07:14:12.283 回答