文章目录
- 前言
- 猜想来源
- 验证方法
- @Controller 的情况
- @ServerEndpoint 的情况
- 后记
前言
最近有 websocket 的需求。探索 @ServerEndpoint
的类成员变量特点。
这里类比 @Controller
讨论 @ServerEndpoint
类成员变量是否线程安全。
猜想来源
网上的教程大多数都这么展示程序:
@Component
@ServerEndpoint(value = "/ws")
public class MyWsEndpoint {private final MyContext context = new MyContext();@OnOpenpublic void onOpen(Session session) {}// ... 省略
}
但是,同样的写法,类比 @Controller
,必然会产生线程安全问题。
@Controller
public class MyController {private static final Logger logger = LoggerFactory.getLogger(MyController.class);private final MyContext context = new MyContext();@GetMapping("/normal")@ResponseBodypublic String normal() {}
}
需要一种快速验证的方式, @ServerEndpoint
的类成员变量则线程安全
验证方法
- 最简单的用例,把读和写拆成两个步骤,如果读到了其他线程的值,则为不安全的情况
// 每个请求独立自增int currentCount = ac.incrementAndGet();// 多线程写context.setCurrentCount(currentCount);// 多线程读if (context.getCurrentCount() != currentCount) {logger.error("线程不安全!!!");}
- 浏览器触发请求,多线程 debug 打断点 制造并发场景
@Controller 的情况
- 线程不安全
原因是@Controller
的类默认是单例的
@ServerEndpoint 的情况
贴一个前端测试代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>ws client</title>
</head>
<body></body><script>let ws = new WebSocket("ws://localhost:8080/ws")ws.onopen = function () {ws.send("hello")}ws.onmessage = function (message) {console.log(message)}
</script>
</html>
- 线程安全
后记
- 上文验证了
@ServerEndpoint
的类成员变量是线程安全的。 - 但是存在矛盾:
- 上面用例
@Component
和@ServerEndpoint
共同作用于一个类上 - Spring 容器管理
@Component
默认是单例的
- 上面用例
- 解释上文的矛盾:
- 【Spring】@ServerEndpoint 与 Spring 是如何集成的,
@Component
用于bean相关的api读取 @ServerEndpoint
使用的是servlet的模式,与Spring mvc 不同,在Filter里面做了new操作
- 【Spring】@ServerEndpoint 与 Spring 是如何集成的,