我需要ServletContext
从 a@ServerEndpoint
中获取,以便找到 SpringApplicationContext
并查找 Bean。
目前我最好的方法是将该 bean 绑定到 JNDI 命名上下文中并在Endpoint
. 欢迎任何更好的解决方案。
我也在寻找一种合理的方式来同步 servletHttpSession
和 websocket 的Session
.
我需要ServletContext
从 a@ServerEndpoint
中获取,以便找到 SpringApplicationContext
并查找 Bean。
目前我最好的方法是将该 bean 绑定到 JNDI 命名上下文中并在Endpoint
. 欢迎任何更好的解决方案。
我也在寻找一种合理的方式来同步 servletHttpSession
和 websocket 的Session
.
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>
的源代码中获得灵感(链接在展示的最底部)。
更新了 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();
// ...
}
}
我在 Tomcat(版本 7.0.56 和 8.0.14)上试用了 BalusC 的答案。在这两个容器上, modifyHandshake 的请求参数不包含 HttpSession(因此不包含 servletContext)。由于我只需要 servlet 上下文来访问“全局”变量(即 Web 应用程序全局变量),因此我只是将这些变量存储在持有者类的普通静态字段中。这是不优雅的,但它工作。
这看起来像是这个特定 tomcat 版本中的一个错误 - 有没有人也看到过这个?
有时我们无法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()。