关键词:
背景
在一个Springboot项目中,写了一个WebSocket服务端代码。具体代码网上一大堆,这里不再展示。同时,我在Websocket服务端的类里面,定义了一个Boolean类型的成员变量。当客户端websocket传来的参数是666时,将该成员变量改为true。客户端传来其他值时,将该成员变量改为false。
在调试中发现,每当客户端新创建一个连接,调用服务端@OnOpen修饰的方法时,服务端的类都是新的一个对象,并不是加入Spring中管理的那个对象。所以每次调用@OnOpen方法时,Boolean成员变量的默认值都为Null,不管之前是否设置为false,新建客户端连到服务端后,都是新的对象。
这时,我发现了服务端的类里,计算在线人数时,用的static修饰的成员变量。这也再次证明,每次新建连接,Websocket服务端的类都新建个对象,并不是用Spring管理的那个单例对象。所以计算在线人数时,才会用static来统计不同对象的连接次数,即在线人数。
扫描@ServerEndpoint注解源码分析
虽然通过表象,已经看到了其原因。但是Spring中既然已经有了WebSocket服务端的一个类,而每次新建客户端还要重新生成一个类,这种操作很难理解,所以下面找到新建对象的这个源码,来落实这个猜测。
首先,SpringBoot整合了WebSocket,我们到springboot-autoconfiguration包里的spring.factories文件里,找到SpringBoot自动配置了关于WebSocket的哪些类,找到如下:
那这三个类,到底用了哪个类呢,分别点进去,打个断点,看项目启动过程中,执行了哪个断点,就用了哪个类。最后发现,是进了如下类的断点:
即在我的项目中,SpringBoot使用了WebSocketServletAutoConfiguration这个类来自动装配WebSocket。
下面对这个类进行分析:
先看这个类上的注解:
重点是@ConditionalOnClass注解,它表示项目的classpath里如果有参数里配置的类,则就加载注解修饰的类。我们看该注解的参数ServerContainer.class这个类:
public interface ServerContainer extends WebSocketContainer
public abstract void addEndpoint(Class<?> clazz) throws DeploymentException;
public abstract void addEndpoint(ServerEndpointConfig sec) throws DeploymentException;
可见,其是WebSocketContainer的一个子类,因为项目里引入了WebSocket的jar包,所以项目里一定有WebSocketContainer这个类,所以SpringBoot项目就加载了WebSocketServletAutoConfiguration自动配置类。
下面看WebSocketServletAutoConfiguration里的注入代码,即刚才进入断点的代码:
可见,其往Spring容器中加入了TomcatWebSocketServletWebServerCustomizer类。下面分析这个类:
public class TomcatWebSocketServletWebServerCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered
@Override
public void customize(TomcatServletWebServerFactory factory)
factory.addContextCustomizers((context) -> context.addServletContainerInitializer(new WsSci(), null));
@Override
public int getOrder()
return 0;
看它源码,一脸懵逼,不知道在干啥,那就看它类继承结构:
看WebServerFactoryCustomizer源码:
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.web.server;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* Strategy interface for customizing @link WebServerFactory web server factories. Any
* beans of this type will get a callback with the server factory before the server itself
* is started, so you can set the port, address, error pages etc.
* <p>
* Beware: calls to this interface are usually made from a
* @link WebServerFactoryCustomizerBeanPostProcessor which is a
* @link BeanPostProcessor (so called very early in the ApplicationContext lifecycle).
* It might be safer to lookup dependencies lazily in the enclosing BeanFactory rather
* than injecting them with @code @Autowired.
*
* @param <T> the configurable web server factory
* @author Phillip Webb
* @author Dave Syer
* @author Brian Clozel
* @since 2.0.0
* @see WebServerFactoryCustomizerBeanPostProcessor
*/
@FunctionalInterface
public interface WebServerFactoryCustomizer<T extends WebServerFactory>
/**
* Customize the specified @link WebServerFactory.
* @param factory the web server factory to customize
*/
void customize(T factory);
重点是其注释:
自定义WebServerFactory类的策略接口。这个类的所有beans在服务启动前,都会执行一个回调函数,所以可以设置端口、地址和错误页等。
注意:对该接口的调用通常由WebServerFactoryCustomizerBeanProcessor进行(所以在SpringContext周期的早期进行),所以使用懒加载创建属性比用@Autowired更安全
从注释中可知,WebServerFactoryCustomizerBeanProcessor会调用这个接口,而WebServerFactoryCustomizerBeanProcessor是一个BeanProcessor。在类初始化完成后,就会执行BeanProcessor的方法。
下面回到TomcatWebSocketServletWebServerCustomizer类的这个代码:
可知,这行代码是由Beanprocessor触发执行的。这样,就执行了这行代码。上图中的这行代码,又new WsSci对象,下面看WsSci对象源码:
在类上有一个@HandlesTypes注解,该注解的作用为获取到所有用@ServerEndpoint修饰的类,并且把这些类赋给onStartup方法的clazzes参数上。至此,WebSocket的服务类上的@ServerEndpoint如何扫描到的源码已经找到。
@OnOpen方法每次调用都会创建新对象源码分析
找@OnOpen的源码,是利用了一点技巧。在WsServerContainer类里,直接搜索关键字open,找到了如下代码:
这里的意思就是将@OnOpen对应的方法,放入到了sec对象的userProperties属性中。这是个映射操作。那么一定会有找这个映射的代码,所以断点打到getUserProperties方法那里,去调试程序,如下图:
等到代码执行到这里后,然后一句一句往下debug,来找到执行@OnOpen方法的代码,最终看到确实是new出了一个新对象。
具体过程不再展示。
总结
下面复盘一下SpringBoot是如何自动装配WebSocket的:
首先,项目依赖了WebSocket的相关jar包,SpringBoot自动注入了一个类WebSocketServletAutoConfiguration。
这个自动装配类,又会往Spring容器加入一个TomcatWebSocketServletWebServerCustomizer类。TomcatWebSocketServletWebServerCustomizer类会被BeanProcessor执行。所以,这个类的方法里,创建了WebSocket的WsSci对象,在这个对象里,搜集了@ServerEndpoint注解,解析了WebSocket的服务类。
所以,本质还是通过BeanProcessor,来解析的@ServerEndpoint注解,不过,是利用了现有的一个BeanProcessor,自定义了一个BeanProcessor操作的类来进行了注解的解析。
springboot整合websocket实现登录挤退现象
在项目期间遇到了同一个账号不能在不同的地方同时登录的情况,解决方法用到了websocket。关于websocket的原理网上有很多,我这里就不写了,推荐博客:https://www.cnblogs.com/myzhibie/p/4470065.html这里我主要记录一下websocket来实现的登... 查看详情
springboot整合websocket简单聊天室(代码片段)
springboot整合websocket(一)简单聊天室springboot整合websocket(一)简单聊天室springboot整合websocket(二)上传文件(引导篇)springboot整合websocket(三)上传文件(终篇& 查看详情
springboot整合websocket实现简单聊天室(代码片段)
项目结构:效果展示:实现步骤步骤一:添加依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency> 查看详情
springboot整合websocket,使用stomp协议,前后端整合实战(代码片段)
... 一篇极简风,最最最基础的方式整合websocket:《SpringBoot整合WebSocket简单实战案例》地址: https://blog.csdn.net/qq_35387940/article/details/ 查看详情
springboot整合websocket
...t:在浏览器和服务器之间建立tcp连接,实现全双工通信??springboot使用websocket有两种方式,一种是实现简单的websocket,另外一种是实现STOMP协议。这一篇实现简单的webso 查看详情
springboot整合elasticsearch和mysql附案例源码
...arch7.8.1从入门到精通的(点我直达),但是还没有整合到SpringBoot中,下面演示将ElasticSearch和mysql整合到SpringBoot中,附演示源码。项目介绍模仿NBA网站网址地址:点我直达 接口开发将数据库数据导入到ElasticSearch通过姓名查找... 查看详情
springboot2系列教程(十六)|整合websocket实现广播
前言如题,今天介绍的是SpringBoot整合WebSocket实现广播消息。什么是WebSocket?WebSocket为浏览器和服务器提供了双工异步通信的功能,即浏览器可以向服务器发送信息,反之也成立。WebSocket是通过一个socket来实现双工异步通信能力... 查看详情
springboot2.0源码分析:整合activemq分析(代码片段)
SpringBoot具体整合ActiveMQ可参考:SpringBoot2.0应用(二):SpringBoot2.0整合ActiveMQActiveMQ自动注入当项目中存在javax.jms.Message和org.springframework.jms.core.JmsTemplate着两个类时,SpringBoot将ActiveMQ需要使用到的对象注册为Bean,供项目注入使用... 查看详情
websocket教程springboot+maven整合(目录)
...、课程技术选型和浏览器兼容讲解简介:简单介绍什么是springboot、socketjs、stompjs,及解决使用浏览器兼容问题3、websocket广播、单播、组播介绍和使用场景说明简介:主要讲解websocket的一些概念,如广播,单播等,他们的基本区... 查看详情
springboot整合websocket遇到的坑
参考技术A如果客户端关闭了websocket,但服务端没有监听到关闭事件,即onClose方法没有调用,这是会发生的情况此时如果服务端向客户端推送消息,会出现异常告诉开发者:关闭了一个连接,并重新调用onClose方法 查看详情
springboot1.5.9整合websocket
一.WebSocket介绍 1.WebSocket是什么? WebSocket是协议,是HTML5开始提供的基于TCP(传输层)的一种新的网络协议, 它实现了浏览器与服务器全双工(full-duplex)通信,即允许服务器主动发送消息给客户端 WebSocket使得客... 查看详情
springboot整合websocket实现实时消息推送(代码片段)
0.开发环境JDK:1.8SpringBoot:2.1.1.RELEASE1.引入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>< 查看详情
springboot整合websocket实现即时聊天功能
...期,公司需要新增即时聊天的业务,于是用websocket整合到Springboot完成业务的实现。一、我们来简单的介绍下websocket的交互原理:1.客户端先服务端发起websocket请求;2.服务端接收到请求之后,把请求响应返回给客户端;3.客户端... 查看详情
springboot2系列教程(十七)|整合websocket实现聊天室
...基础之上,为便于更好理解今天这一篇,推荐先阅读:「SpringBoot整合WebSocket实现广播消息」 查看详情
springboot整合websocket时,自动注入service层报错空指针异常的解决方案
1.SpringBoot整合WebSocket时,自动注入Service层报错空指针异常的解决方案1.1异常重现1.2分析原因现象分析:我们在spring或springboot的websocket里面使用@Autowired注入service或bean时,会报空指针异常,获取的service为nullÿ... 查看详情
springboot整合websocket实现实时消息推送(代码片段)
0.开发环境JDK:1.8SpringBoot:2.1.1.RELEASE1.引入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>2.新建WebSocket配置类importorg.springframework.context.annotatio... 查看详情
duboo3.0+springboot+zookeeper整合例子(附源码)(代码片段)
dubbo3.0整合SpringBoot例子dubbo新版本(3.0以上)在相对于dubbo旧版本(2.5、2.6、2.7),有很多的不相同的地方。官方文档也说了新版本的特性:https://dubbo.apache.org/zh/docs/v3.0/new-in-dubbo3本文就来使用dubbo3.0新版本... 查看详情
springboot整合websocket,使用stomp协议+redis解决负载场景问题(代码片段)
前言上一篇,简单给大家整合了一下websocket,使用stomp方式。这篇,就是考虑到单体的服务使用websocket,按照上一篇的整合,确实没问题。但是如果一旦是负载多台服务的时候,那么就会出现丢失问题。什... 查看详情