最近在项目中需要使用WebSocket,因为项目是使用的SpringBoot架构,所以集成比较简单。
上代码:
1 2 3 4 | < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-websocket</ artifactId > </ dependency > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; /** * web socket 工具类 * * @author alvinqiu * @data 2018/10/24 */ @Slf4j @Component @ServerEndpoint (value = "/ws/{type}" ) public class WebSocketServer { /** * concurrent包的线程安全Set,用来存放每个客户端对应的Session对象 */ private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>(); @OnOpen public void onOpen( @PathParam ( "type" ) String type, Session session) { SessionSet.add(session); log.info( "WebSocket有连接加入, 请求的数据类型为: " + type); try { sendMessage( "Hello WebSocket " + type); } catch (IOException e) { e.printStackTrace(); } } @OnClose public void onClose(Session session) { SessionSet.remove(session); log.info( "WebSocket有连接关闭" ); } @OnError public void onError(Throwable error) { log.info( "WebSocket发生错误, 原因: " + error.getMessage()); } @OnMessage public void onMessage(String message) { log.info( "收到来自WebSocket客户端的消息: " + message); } /** * 发送消息给客户端 * * @param message * @throws IOException */ public void sendMessage(String message) throws IOException { for (Session session : SessionSet) { if (session.isOpen()) { session.getBasicRemote().sendText(message); } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * web socket 配置类 * * @author alvinqiu * @data 2018/10/24 */ @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } |
客户端就不写了,上面的代码也没什么好说的,相当简单,项目跑起来就完事儿咯
结果就这简单玩意儿出现了问题
java.lang.IllegalStateException: Failed to register @ServerEndpoint class: class com.xxx.WebSocketServer$$EnhancerBySpringCGLIB$$62689f33
javax.websocket.DeploymentException: Cannot deploy POJO class [com.xxx.WebSocketServer$$EnhancerBySpringCGLIB$$62689f33] as it is not annotated with @ServerEndpoint
报错了?怎么会呢,这么简单的一个玩意儿,网上的教程不都是这么干的吗
研究下错误,标注了@ServerEndPoint注解的类注册失败,百思不得其解,百度谷歌一番,什么jdk版本、tomcat版本、需不需要注入ServerEndpointExporter等等等等,我项目情况都不是(jdk1.8、tomcat8+、springboot内置容器)
后来想想是否因为项目是多模块结构的原因,可能是jar包冲突、循环依赖,什么可能都想过,然后新建了一个springboot项目,只集成websocket试试,结果是正常运行的,版本一致,环境一致,代码一致,问题看来出在项目本身了
父工程
A 模块(启动模块,依赖了其他所有子模块,一些公共的配置类)
B 模块(业务模块)
...
...
Q 模块(WebSocket模块)
难道是WebSocket作为一个单独模块出的飞机? 这么扯的理由我也不管了,还是把它挪到了A 模块去,结果问题依旧....
前后折腾了很久,终于决定跟跟源码
恩,就是这里annotation为null,至于为何为null还是不知道,突发奇想去看看另一个springboot项目(成功案例),
恩,annotation是有值的
(报错)
class com.xxx.websocket.WebSocketServer$$EnhancerBySpringCGLIB$$62689f33
(成功)
class com.xxx.websocket.WebSocketServer
后面那一串是代表类被cglib转换为了代理对象,什么情况下会被转换为代理对象?
AOP!AOP!AOP!
简直是如梦初醒,虎躯一震
我的 A 模块里是写了一个全局的log日志切面
大概是这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /** * 日志切面 * * @author alvinqiu * @date 2018/09/28 */ @Slf4j @Aspect @Component public class AspectLog { @Pointcut ( "execution(* com.xxx..*.*(..))" ) public void aspectLog() { } @Pointcut ( "execution(* com.xxx.api..*.*(..))" ) public void aspectApiLog() { } @Pointcut ( "execution(* com.xxx.service..*.*(..))" ) public void aspectServiceLog() { } @Pointcut ( "execution(* com.xxx.mapper..*.*(..))" ) public void aspectMapperLog() { } ... ... } |
问题找出来咯,WebSocketServer这个类被切咯,以至于@ServerEndPoint注解无法注入至对应的对象,导致报错
AOP 排除此类
1. 出问题多看看源码
2. 多与大神交流,碰撞,无论多么难以描述的问题,全世界都对,你的项目不对,也要与大神沟通,一点点的终究会得到思路