spring boot websocket @OnMessage中使用@Autowired spring bean报null错误
有问题的写法@ClientEndpoint@Componentpublic class SmkCenterConsumer {@Autowiredprivate SmkCenterDataRepository repository;@OnMessagepublic void onMessage(String message) {...
有问题的写法
@ClientEndpoint
@Component
public class SmkCenterConsumer {
@Autowired
private SmkCenterDataRepository repository;
@OnMessage
public void onMessage(String message) {
repository.save(data);
}
}
报错:
[ ERROR] [2020-03-11 11:41:36] org.apache.tomcat.websocket.pojo.PojoEndpointBase [175] - No error handling configured for [SmkCenterConsumer] and the following error occurred
java.lang.NullPointerException: null
at SmkCenterConsumer.onMessage(SmkCenterConsumer.java:45)
正确的写法
@ClientEndpoint
@Component
public class SmkCenterConsumer {
private static SmkCenterDataRepository repository;
@Autowired
public void setRepository(SmkCenterDataRepository repository) {
SmkCenterConsumer.repository = repository;
}
@OnMessage
public void onMessage(String message) {
repository.save(data);
}
}
背景知识
在spring boot中引入
compile('org.springframework.boot:spring-boot-starter-websocket')
实际上涉及到下面三个层次的知识
java websocket API(JSR-356)
开发 WebSocket 的Java API 集合,比如javax.websocket.Endpoint
spring websocket抽象
实际上spring对websocket进行了一些api的抽象
官方文档参考:websocket
比如:org.springframework.web.socket.WebSocketHandler
javadoc
A handler for WebSocket messages and lifecycle events.
spring无非就是把javax.websocket.Endpoint在onMessage的时候将相应的数据进行读取,传递到handleMessage()这个方法中,通过模板方法模式来重载
spring boot整合websocket
springboot使用编程方式javax.websocket.server.ServerContainer来部署websocket endpoint
参考:spring boot中websocket endpoint是如何初始化及启动的
原因
通过jstack,查看线程,可以看到,每一个@ClientEndPoint实际上对应一个线程WebSocketClient-AsyncIO-digit(数字)
org.apache.tomcat.websocket.AsyncChannelGroupUtil
/**
* This is a utility class that enables multiple {@link WsWebSocketContainer}
* instances to share a single {@link AsynchronousChannelGroup} while ensuring
* that the group is destroyed when no longer required.
*/
public class AsyncChannelGroupUtil {
// 使用线程池
ExecutorService executorService = new ThreadPoolExecutor(
0,
Integer.MAX_VALUE,
Long.MAX_VALUE, TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable>(),
new AsyncIOThreadFactory());
......
// 启动线程
@Override
public Thread run() {
Thread t = new Thread(r);
t.setName("WebSocketClient-AsyncIO-" + count.incrementAndGet());
t.setContextClassLoader(this.getClass().getClassLoader());
t.setDaemon(true);
return t;
}
}
可以看到,这个线程是tomcat启动的
在这个tomcat启动的线程中如何使用spring容器提供的@Autowired的单例bean呢?
如果不是static,这个repository就是null
在这个线程中也没有办法从spring容器中取到这个bean,所以只能把这个bean设置为static,这样这个单例bean就脱离了spring容器的限制,可以在所有线程中使用了
另一个思路:实现BeanFactoryAware,这样可以通过注入的BeanFactory拿到这个bean,应该也是可行的
更多推荐
所有评论(0)