Java for Web学习笔记(七二):Service和Repository(7)在Spring框架中使用WebSocket
来源:互联网 发布:php while 死循环 编辑:程序博客网 时间:2024/06/05 08:52
在WebSocket中注入spring bean
和Listener一样,websocket不属于spring framework,不是spring framework天然的bean,因此无法在里面直接注入spring的bean。
SpringCongifurator
为了让websocket更好地在Spring framework中工作,WebSocket Endpoint的配置器将继承SpringCongifurator。我们需要在pom.xml中引入相关的jar包:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${spring.framework.version}</version> <scope>compile</scope></dependency>
我们可以通过SpringCongifurator作为websocket endpoint的配置器
@ServerEndpoint(value = "/chat/{sessionId}", configurator = SpringCongifurator.class)public class ChatEndpoint { ... ...}
如果需要自定义配置器,具体见下面代码:
@ServerEndpoint(value = "/chat/{sessionId}", encoders = ChatMessageCodec.class, decoders = ChatMessageCodec.class, configurator = ChatEndpoint.EndpointConfigurator.class)public class ChatEndpoint { @Inject SessionRegistryService sessionRegisterService; ... ... // SpringConfigurator是继承了ServerEndpointConfig.Configurator(参见 WebSocket(5):encoder,decoder和configurator#configurator) // 本例,我们将httpSession存放在websocket.Session的user Properties中,因此采用自定义的方式。为了便于获取,我们提供了两个静态方法。当然我们可以使用 webSocketSession.getUserProperties().get(key) 来获取 // 通过SpringConfigurator,我们就可以在ChatEndpoint中使用Spring的注入,如上面的SessionRegistryService,但ChatEndpoint并不属于Spring framework,不能作为Bean被注入。如果我们想将ChatEndpoint作为一个Bean,可以在RootContext的配置中 // @Bean // public ChatEndpoint chatEndpoint(){ // return new ChatEndpoint(); // } // 但是这样一来系统就只有一个ChatEndpoint管理所有的连接,而不是每个ChatEndpoint管理一条连接,处理起来要复杂很多。 public static class EndpointConfigurator extends SpringConfigurator{ private static final String HTTP_SESSION_KEY = "cn.wei.flowingflying.customer_support.http.session"; private static final String PRINCIPAL_KEY = "cn.wei.flowingflying.customer_support.user.principal"; @Override public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) { super.modifyHandshake(config, request, response); HttpSession session = (HttpSession)request.getHttpSession(); config.getUserProperties().put(HTTP_SESSION_KEY, session); config.getUserProperties().put(PRINCIPAL_KEY, UserPrincipal.getPrincipal(session)); } private static HttpSession getExposedSession(Session session){ return (HttpSession) session.getUserProperties().get(HTTP_SESSION_KEY); } private static Principal getExposedPrincipal(Session session){ return (Principal)session.getUserProperties().get(PRINCIPAL_KEY); } }}
在websocket中使用Scheduler
使用websocket,一般情况下,一个连接对应要给websocket实例,也就是我们不采用在root context中配置websocket为bean,这会导致所有的连接都对应到同一个websocket实例中。也就是websocket实例不作为singleton bean。而之前学习的@Scheduled必须加载singleton bean上,我们需要另辟方法。
下面的例子我们在websocket中通过taskSechdule启动ping-pong机制,在chat结束时终止。在例子中我们接上一学习的例子,演示了在http session删除时,结束chat,重点看看如何使用consumer。
@ServerEndpoint(value = "/chat/{sessionId}", encoders = ChatMessageCodec.class, decoders = ChatMessageCodec.class, configurator = ChatEndpoint.EndpointConfigurator.class)public class ChatEndpoint { private static final byte[] pongData = "This is PONG country.".getBytes(StandardCharsets.UTF_8); private ScheduledFuture<?> pingFuture; @Inject SessionRegistryService sessionRegisterService; //我们不能直接在sendPing()前加入@Scheduled,pring的@Schedule只支持singleton bean,而ChatEndpoint不是,我们没有采用在root Context中将其设置为bean,而是每条连接都有一个实例。因此,我们将直接使用TaskScheduler。 @Inject TaskScheduler taskScheduler; // 在sessionRegisterService的登记和注销httpSession删除的的回调函数,如果我们不将其赋给一个对象,Method Reference中this::httpSessionRemoved每次的值并不一样,要确保注册和注销中是同一个回调函数,需要将其固化。 private final Consumer<HttpSession> callback = this::httpSessionRemoved; /* 用于在注入后执行。对于支持注入的类都会支持@PostConstruct,不仅是spring,WebSocket的ServerEndpoint支持。在接收到websocket client的连接请求的时候,server endpoint对象被创建,然后执行@postConstruct */ @PostConstruct public void initialize(){ this.sessionRegisterService.registerOnRemoveCallback(this.callback); this.pingFuture = this.taskScheduler.scheduleWithFixedDelay( this::sendPing, new Date(System.currentTimeMillis() + 25_000L), // start time 25_000L); //delay } // httpSession删除时的处理回调函数。 private void httpSessionRemoved(HttpSession httpSession){ if(httpSession == this.httpSession){ synchronized(this){ if(this.closed) return; log.info("Chat session ended abruptly by {} logging out.", this.principal.getName()); this.close(ChatService.ReasonForLeaving.LOGGED_OUT, null); } } } //具体处理chat关闭 private void close(ChatService.ReasonForLeaving reason, String unexpected){ this.closed = true; if(this.pingFuture.isCancelled()) this.pingFuture.cancel(true); this.sessionRegisterService.deregisterOnRemoveCallback(this.callback); } private void sendPing(){//发送ping ... ... this.wsSession.getBasicRemote().sendPing(ByteBuffer.wrap(ChatEndpoint.pongData)); } @OnMessage public void onPong(PongMessage message){ //收到pong ByteBuffer data = message.getApplicationData(); if(!Arrays.equals(ChatEndpoint.pongData, data.array())) log.warn("Received pong message with incorrect payload."); else log.debug("Received good pong message."); } ... ...}
相关链接: 我的Professional Java for Web Applications相关文章
阅读全文
0 0
- Java for Web学习笔记(七二):Service和Repository(7)在Spring框架中使用WebSocket
- Java for Web学习笔记(七一):Service和Repository(6)在Spring框架中使用Listener
- Java for Web学习笔记(七三):国际化i18n(1)使用Spring框架MessageSource
- Java for Web学习笔记(四七):WebSocket(4)Java Client和二进制消息
- Java for Web学习笔记(六六):Service和Repository(1)抽象分层
- Java for Web学习笔记(六七):Service和Repository(2)抽象分层例子
- Java for Web学习笔记(六九):Service和Repository(4)Principal
- Java for Web学习笔记(七十):Service和Repository(5)回调处理Consumer
- Java for Web学习笔记(六八):Service和Repository(3)异步Async和调度Schedule
- Java for Web学习笔记(二十):Session(4)在集群中使用Session
- Java for Web学习笔记(二七):JSTL(3)Core Tag(中)
- Java for Web学习笔记(五二):Spring框架简介(1)特点简述
- Spring学习之使用标签来标记资源(@Component、@Repository、 @Service和@Controller)以及使用方式(包含如何在jsp中使用)
- Java for Web学习笔记(四八):WebSocket(5)encoder,decoder和configurator
- Java for Web学习笔记(五八):Spring框架简介(7)bean的profile
- Java for Web学习笔记(十五):JSP(5)在JSP中使用Java吗?
- Java for Web学习笔记(九十):消息和集群(5)利用websocket实现订阅和发布(上)
- Java for Web学习笔记(五三):Spring框架简介(2)一些准备
- 一文看懂迁移学习:怎样用预训练模型搞定深度学习?
- HTML+CSS进阶学习摘录(性能优化)(七)
- Java异常
- codeforce 25D
- JavaScript之遍历
- Java for Web学习笔记(七二):Service和Repository(7)在Spring框架中使用WebSocket
- C#中 ArrayList的使用
- 前端日期选取插件bootstrap-datepicker.js的使用
- BZOJ 2957 楼房重建(线段树)(思路)
- solr-8 RequestHandlers and SearchComponents in SolrConfig
- 22.生成所有的括号组合
- Android Studio :fetching documentation的问题
- java的jvm和操作系统的关系
- Canvas学习笔记之画线