SpringBoot整合WebSocket,启动时报错......
原创
©著作权归作者所有:来自51CTO博客作者会下蛋的咯咯的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、问题描述
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2024-05-12 15:40:13.959 ERROR 21560 --- [ main] o.s.boot.SpringApplication : Application run failed
java.lang.IllegalStateException: Failed to register @ServerEndpoint class: class com.example.baidu.speech.websocket.BaiduSpeechRealtimeWebSocket
at org.springframework.web.socket.server.standard.ServerEndpointExporter.registerEndpoint(ServerEndpointExporter.java:159) ~[spring-websocket-5.3.23.jar:5.3.23]
at org.springframework.web.socket.server.standard.ServerEndpointExporter.registerEndpoints(ServerEndpointExporter.java:134) ~[spring-websocket-5.3.23.jar:5.3.23]
at org.springframework.web.socket.server.standard.ServerEndpointExporter.afterSingletonsInstantiated(ServerEndpointExporter.java:112) ~[spring-websocket-5.3.23.jar:5.3.23]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:974) ~[spring-beans-5.3.23.jar:5.3.23]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.23.jar:5.3.23]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.13.jar:2.6.13]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745) [spring-boot-2.6.13.jar:2.6.13]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:420) [spring-boot-2.6.13.jar:2.6.13]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.6.13.jar:2.6.13]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1317) [spring-boot-2.6.13.jar:2.6.13]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) [spring-boot-2.6.13.jar:2.6.13]
at com.example.baidu.speech.BaiduSpeechApplication.main(BaiduSpeechApplication.java:12) [classes/:na]
Caused by: javax.websocket.DeploymentException: No Throwable parameter was present on the method [onError] of class [com.example.baidu.speech.websocket.BaiduSpeechRealtimeWebSocket] that was annotated with OnError
at org.apache.tomcat.websocket.pojo.PojoMethodMapping.getPathParams(PojoMethodMapping.java:354) ~[tomcat-embed-websocket-9.0.68.jar:9.0.68]
at org.apache.tomcat.websocket.pojo.PojoMethodMapping.<init>(PojoMethodMapping.java:223) ~[tomcat-embed-websocket-9.0.68.jar:9.0.68]
at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:157) ~[tomcat-embed-websocket-9.0.68.jar:9.0.68]
at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:281) ~[tomcat-embed-websocket-9.0.68.jar:9.0.68]
at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:231) ~[tomcat-embed-websocket-9.0.68.jar:9.0.68]
at org.springframework.web.socket.server.standard.ServerEndpointExporter.registerEndpoint(ServerEndpointExporter.java:156) ~[spring-websocket-5.3.23.jar:5.3.23]
... 12 common frames omitted
二、解决方法
== 刚开始没注意到这么一小段日志,害得我调了一波源码,服了。。。
No Throwable parameter was present on the method [onError] of class [com.example.baidu.speech.websocket.BaiduSpeechRealtimeWebSocket] that was annotated with OnError
==
意思就是 @OnError 注解方法没有 Throwable 参数。解决如下。
@OnError
public void onError(Throwable throwable) { // 添加 Throwable 参数
log.error("BaiduSpeechRealtimeWebSocket error...", throwable);
}
三、详细过程
1、debug图
2、源码调试过程
// 控制台异常日志
1、org.springframework.web.socket.server.standard.ServerEndpointExporter#registerEndpoint(java.lang.Class<?>)
2、org.apache.tomcat.websocket.server.WsServerContainer#addEndpoint(java.lang.Class<?>)
3、org.apache.tomcat.websocket.pojo.PojoMethodMapping // PojoMethodMapping的构造方法
代码片段 ==> [onCloseParams = getPathParams(onClose, MethodType.ON_CLOSE);]
// 本次问题关键方法
4、org.apache.tomcat.websocket.pojo.PojoMethodMapping#getPathParams
3、关键的方法源码【org.apache.tomcat.websocket.pojo.PojoMethodMapping#getPathParams】
// 这个方法比较有意思,可以看看
private static PojoPathParam[] getPathParams(Method m,
MethodType methodType) throws DeploymentException {
if (m == null) {
return new PojoPathParam[0];
}
boolean foundThrowable = false;
Class<?>[] types = m.getParameterTypes();
Annotation[][] paramsAnnotations = m.getParameterAnnotations();
PojoPathParam[] result = new PojoPathParam[types.length];
// 方法有几个参数就循环几次
// 从这个循环中可以看出@OnOpen、@OnClose、@OnMessage、@OnError分别可以设置哪些自带参数
for (int i = 0; i < types.length; i++) {
Class<?> type = types[i];
if (type.equals(Session.class)) {
result[i] = new PojoPathParam(type, null);
} else if (methodType == MethodType.ON_OPEN &&
type.equals(EndpointConfig.class)) {
result[i] = new PojoPathParam(type, null);
} else if (methodType == MethodType.ON_ERROR
&& type.equals(Throwable.class)) {
// @OnError注解方法,且带有Throwable参数,则foundThrowable置为true
foundThrowable = true;
result[i] = new PojoPathParam(type, null);
} else if (methodType == MethodType.ON_CLOSE &&
type.equals(CloseReason.class)) {
result[i] = new PojoPathParam(type, null);
} else {
Annotation[] paramAnnotations = paramsAnnotations[i];
for (Annotation paramAnnotation : paramAnnotations) {
if (paramAnnotation.annotationType().equals(
PathParam.class)) {
result[i] = new PojoPathParam(type,
((PathParam) paramAnnotation).value());
break;
}
}
// Parameters without annotations are not permitted
if (result[i] == null) {
throw new DeploymentException(sm.getString(
"pojoMethodMapping.paramWithoutAnnotation",
type, m.getName(), m.getClass().getName()));
}
}
}
// 加了@OnError注解,但是没有Throwable参数,就直接报错
if (methodType == MethodType.ON_ERROR && !foundThrowable) {
throw new DeploymentException(sm.getString(
"pojoMethodMapping.onErrorNoThrowable",
m.getName(), m.getDeclaringClass().getName()));
}
return result;
}