我需要从ServletContext
中获取@ServerEndpoint
才能找到Spring ApplicationContext
并查找Bean。
目前,我最好的方法是在JNDI命名上下文中绑定该bean并在Endpoint
中查找它。欢迎任何更好的解决方案。
我也在寻找一种合理的方法来将servlet的HttpSession
与websocket的Session
同步。
答案 0 :(得分:38)
HandshakeRequest#getHttpSession()
中的servlet HttpSession
位于JSR-356中,当@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);
}
}
以下是如何使用它,请注意@ServerEndpoint
的{{3}}属性:
@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),您可以从configurator
的源代码中获取灵感(链接位于展示的最底部)。
答案 1 :(得分: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();
// ...
}
}
答案 2 :(得分:0)
我在Tomcat(版本7.0.56和8.0.14)上尝试了BalusC的答案。在两个容器上,modifyHandshake的请求参数不包含HttpSession(因此不包含servletContext)。 因为我需要servlet上下文才能访问" global"变量(web-application global,即),我只是将这些变量存储在holder类的普通静态字段中。这是不优雅的,但它确实有效。
这就像这个特定的tomcat版本中的一个bug - 有没有人在那里看过这个?
答案 3 :(得分:0)
有时我们无法获得BalusC的session
以上的ServletAwareConfig
,这是因为仍未创建会话。由于我们不是在寻求会话而是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()。