@ServerEndpoint
内部获取ServletContext
,以便找到Spring的ApplicationContext
并查找一个Bean。目前,我最好的方法是将该Bean绑定在JNDI命名上下文中,并在
Endpoint
中查找它。欢迎任何更好的解决方案。我还在寻找一种合理的方法来同步Servlet的
HttpSession
和websocket的Session
。@ServerEndpoint
内部获取ServletContext
,以便找到Spring的ApplicationContext
并查找一个Bean。Endpoint
中查找它。欢迎任何更好的解决方案。HttpSession
和websocket的Session
。在JSR-356中,servlet HttpSession
可以通过HandshakeRequest#getHttpSession()
获得,当在一个@ServerEndpoint
之前进行握手请求时,在@OnOpen
之前。
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
的 configurator
属性:
@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>
的源代码中获取灵感(链接位于展示的最底部)。
HandshakeRequest
中添加getServletContext()
方法:请给它点赞 :) - morgwai更新 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();
// ...
}
}
HttpFilter
或ServletRequestListener
并调用request.getSession(true)
即可。遗憾的是,在HandshakeRequest.getHttpSession()
上没有类似的重载。 - earcamServletAwareConfig
获取到session
,这是因为session还没有被创建。由于我们不是在寻找session而是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) {
}
}
}
@ServerEndpoint
注释中使用configurator = org.springframework.web.socket.server.endpoint.SpringConfigurator.class
,它应该是可注入的。 - Peter G