Springboot项目整合WebSocket源码分析

背景

在一个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操作的类来进行了注解的解析。

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

敲代码的小小酥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值