Skip to content

WebSocket 作用域

每个 WebSocket 会话都拥有一组属性(Attributes)。这些属性会作为标头附加到客户端发送的消息中,并可以在控制器方法中访问:

java
@Controller
public class MyController {

	@MessageMapping("/action")
	public void handle(SimpMessageHeaderAccessor headerAccessor) {
		Map<String, Object> attrs = headerAccessor.getSessionAttributes();
		// ...
	}
}

使用 @WebSocketScope

你可以在 Spring 中声明一个具有 websocket 作用域的 Bean。这些 Bean 可以被注入到控制器或在 clientInboundChannel 上注册的任何拦截器中。

由于控制器通常是单例(Singleton),其寿命比 WebSocket 会话长,因此必须以代理模式使用 WebSocket 作用域的 Bean:

java
@Component
@WebSocketScope
public class MyBean {

	@PostConstruct
	public void init() {
		// 在依赖注入后调用
	}

	@PreDestroy
	public void destroy() {
		// 在 WebSocket 会话结束时调用
	}
}

@Controller
public class MyController {

	private final MyBean myBean;

	@Autowired
	public MyController(MyBean myBean) {
		this.myBean = myBean;
	}

	@MessageMapping("/action")
	public void handle() {
		// 此处的 myBean 实例属于当前 WebSocket 会话
	}
}

与任何自定义作用域一样,Spring 会在控制器第一次访问 myBean 时实例化它,并将其存储在会话属性中。直到会话结束前,后续的所有访问都会返回同一个实例。


补充教学

1. 作用域的物理存储

实际上,这些 Bean 实例是被存储在 WebSocket Session 的 attributes 映射表里的。这也意味着,如果你使用了外部消息代理(集群环境),这些 Bean 的数据默认是不能跨服务器共享的,除非你使用了 Spring Session 这种外部存储机制。

2. 生命周期回调的威力

@PostConstruct@PreDestroy 非常强大。你可以用 @PreDestroy 在用户离开时自动清理资源,比如关闭打开的文件句柄、通知其他用户该玩家已下线等。

3. @WebSocketScope 还是 Map?

如果你只需要存几个简单的 key-value,直接用 headerAccessor.getSessionAttributes() 更轻量。如果你需要封装复杂的业务状态(如一个在线游戏的“当前玩家房间信息”对象),使用 @WebSocketScope 的 Bean 能让代码结构更清晰、更符合 Spring 的依赖注入哲学。

Based on Spring Framework.