• SpringBoot集成WebSocket--------Spring方式集成(一)


    一、引入依赖

    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-websocket</artifactId>
       <version>2.7.0</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、配置

    1、WebSocketConfigurer配置器

    /**
     *  实现回调方法registerWebSocketHandlers(会被@EnableWebSocket注解获取并回调)配置WebSocket的请求处理逻辑
     */
    public interface WebSocketConfigurer {
    
    	/**
    	 * 注册WebSocketHandler(WebSocket的核心处理器,可以监听指定端点),也可以设置SockJS回退选项
    	 */
    	void registerWebSocketHandlers(WebSocketHandlerRegistry registry);}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2、@EnableWebSocket注解

    (1)注解引入一个配置类DelegatingWebSocketConfiguration.class

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(DelegatingWebSocketConfiguration.class)
    public @interface EnableWebSocket {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2)DelegatingWebSocketConfiguration配置类会被容器自动加载,同时会注入容器中所有的WebSocketConfigurer实现到configurers中;同时registerWebSocketHandlers()方法会遍历configurers并回调所有WebSocketConfigurer的registerWebSocketHandlers()方法完成WebSocket的核心处理器的配置

    @Configuration(proxyBeanMethods = false)
    public class DelegatingWebSocketConfiguration extends WebSocketConfigurationSupport {
    
    	private final List<WebSocketConfigurer> configurers = new ArrayList<>();
    
    
    	@Autowired(required = false)
    	public void setConfigurers(List<WebSocketConfigurer> configurers) {
    		if (!CollectionUtils.isEmpty(configurers)) {
    			this.configurers.addAll(configurers);
    		}
    	}
    
    
    	@Override
    	protected void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    		for (WebSocketConfigurer configurer : this.configurers) {
    			configurer.registerWebSocketHandlers(registry);
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在父类WebSocketConfigurationSupport中,被@Bean注释的webSocketHandlerMapping()开始初始化时,会从容器中获取一个默认的TaskScheduler(本配置类中使用@Bean注入的),然后通过initHandlerRegistry()创建一个ServletWebSocketHandlerRegistry,在initHandlerRegistry()方法中将创建的ServletWebSocketHandlerRegistry传递给registerWebSocketHandlers(),该方法就是由其子类DelegatingWebSocketConfiguration实现的,在上面的描述中可以看到出DelegatingWebSocketConfiguration 又将ServletWebSocketHandlerRegistry传递给了每一个用户自定义的WebSocketConfigurer的registerWebSocketHandlers()方法,用于配置WebSocketHandler

    public class WebSocketConfigurationSupport {
    
    	@Nullable
    	private ServletWebSocketHandlerRegistry handlerRegistry;
    
    	@Nullable
    	private TaskScheduler scheduler;
    
    
    	@Bean
    	public HandlerMapping webSocketHandlerMapping(@Nullable TaskScheduler defaultSockJsTaskScheduler) {
    		ServletWebSocketHandlerRegistry registry = initHandlerRegistry();
    		if (registry.requiresTaskScheduler()) {
    			TaskScheduler scheduler = defaultSockJsTaskScheduler;
    			Assert.notNull(scheduler, "Expected default TaskScheduler bean");
    			registry.setTaskScheduler(scheduler);
    		}
    		return registry.getHandlerMapping();
    	}
    
    	private ServletWebSocketHandlerRegistry initHandlerRegistry() {
    		if (this.handlerRegistry == null) {
    			this.handlerRegistry = new ServletWebSocketHandlerRegistry();
    			registerWebSocketHandlers(this.handlerRegistry);
    		}
    		return this.handlerRegistry;
    	}
    
    	protected void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    	}
    
    	@Bean
    	@Nullable
    	public TaskScheduler defaultSockJsTaskScheduler() {
    		if (initHandlerRegistry().requiresTaskScheduler()) {
    			ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler();
    			threadPoolScheduler.setThreadNamePrefix("SockJS-");
    			threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors());
    			threadPoolScheduler.setRemoveOnCancelPolicy(true);
    			this.scheduler = threadPoolScheduler;
    		}
    		return this.scheduler;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    注意:如果项目其他地方需要使用TaskScheduler ,并且重复配置了一个TaskScheduler ,这里就会冲突

    3、实现WebSocketConfigurer配置

    我们可以在一个项目中配置多个WebSocketHandler用于处理不同端点的消息(类似Controller接口)

    @Configuration
    @EnableWebSocket
    @ComponentScan(basePackages = "cn.edu1010.websocket.handler", useDefaultFilters = false, includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Component.class))
    public class WebSocketConfig implements WebSocketConfigurer {
    
    	@Override
    	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、注册WebSocketHandler

    1、WebSocketHandler消息处理器接口

    public interface WebSocketHandler {
    
        //WebSocket连接建立成功之后调用
    	void afterConnectionEstablished(WebSocketSession session) throws Exception;
    
        //新的WebSocket消息到达时调用
    	void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception;
    
        //处理来自底层的WebSocket的消息传输错误
    	void handleTransportError(WebSocketSession session, Throwable exception) throws Exception;
    
    	 //WebSocket连接已被任何一方关闭,或者发生传输错误之后调用
    	void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception;
    
    	//是否支持分片消息
    	boolean supportsPartialMessages();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    可以实现WebSocketHandler接口,也可以继承AbstractWebSocketHandler类来创建WebSocketHandler实例

    @Component
    public class SpringSocketHandle extends AbstractWebSocketHandler {
    
    	@Override
    	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    		// TODO Auto-generated method stub
    	}
    
    	/**
    	 * 文本消息体
    	 */
    	@Override
    	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    		// TODO Auto-generated method stub
    	}
    
    	/**
    	 * 二进制消息体
    	 */
    	@Override
    	protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
    		// TODO Auto-generated method stub
    	}
    
    	/**
    	 * Pong 消息体
    	 */
    	@Override
    	protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
    		// TODO Auto-generated method stub
    	}
    
    	@Override
    	public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
    		// TODO Auto-generated method stub
    	}
    
    	@Override
    	public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
    		// TODO Auto-generated method stub
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    2、注册WebSocketHandler

    在创建的WebSocketConfig中使用WebSocketHandlerRegistry注册SpringSocketHandle,并为其指定端点"/endpoint"。这里需要注意WebSocketHandlerRegistry .addHandler(WebSocketHandler webSocketHandler, String… paths)的第二个参数是一个字符串类型的参数列表,这就说明你可以为多个端点指定同样配置的WebSocketHandler处理

    @Configuration
    @EnableWebSocket
    public class WebSocketConfig implements WebSocketConfigurer {
    	@Autowired
    	public SpringSocketHandle springSocketHandle;
    
    	@Override
    	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    	   registry.addHandler(springSocketHandle, "/endpoint");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    四、配置WebSocketHandler

    WebSocketHandlerRegistry.addHandler()方法注册WebSocketHandler之后会返回WebSocketHandlerRegistration用于配置WebSocketHandler

    public interface WebSocketHandlerRegistration {
        //继续添加消息处理器
    	WebSocketHandlerRegistration addHandler(WebSocketHandler handler, String... paths);
        //添加握手处理器,处理握手事件
    	WebSocketHandlerRegistration setHandshakeHandler(HandshakeHandler handshakeHandler);
        //添加握手拦截器,可以在处理握手前和握手后处理一些业务逻辑
    	WebSocketHandlerRegistration addInterceptors(HandshakeInterceptor... interceptors);
        //配置允许的浏览器跨源请求类型
    	WebSocketHandlerRegistration setAllowedOrigins(String... origins);
        //配置允许的浏览器跨源请求类型
    	WebSocketHandlerRegistration setAllowedOriginPatterns(String... originPatterns);
        //允许使用SockJS应急选项
    	SockJsServiceRegistration withSockJS();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    五、WebSocketMessage和WebSocketSession

    1、WebSocketMessage

    public interface WebSocketMessage<T> {
    
    	/**
    	 * 消息载荷
    	 */
    	T getPayload();
    
    	/**
    	 * 消息字节长度
    	 */
    	int getPayloadLength();
    
    	/**
    	 * 当org.springframework.web.socket.WebSocketHandler#supportsPartialMessages()配置允许分片消息时,
    	 * 如果当前消息是客户端本次送达消息的最后一部分时,该方法返回true。如果分片消息不可用或是被禁用,放回false
    	 */
    	boolean isLast();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2、WebSocketSession

    public interface WebSocketSession extends Closeable {
    
    	/**
    	 * 会话标识
    	 */
    	String getId();
    
    	/**
    	 * WebSocket 连接的URI
    	 */
    	@Nullable
    	URI getUri();
    
    	/**
    	 * 返回握手请求中使用的Headers
    	 */
    	HttpHeaders getHandshakeHeaders();
    
    	/**
    	 *返回WebSocke会话关联的属性。
    	 *在服务端,可以使用org.springframework.web.socket.server.HandshakeInterceptor填充属性
    	 *在客户端,可以使用org.springframework.web.socket.client.WebSocketClient的握手方法填充属性
    	 */
    	Map<String, Object> getAttributes();
    
    	/**
    	 * 返回一个包含已验证的用户名称的java.security.Principal实例,如果用户没有验证成功返回null
    	 */
    	@Nullable
    	Principal getPrincipal();
    
    	/**
    	 * 返回请求接收方的地址
    	 */
    	@Nullable
    	InetSocketAddress getLocalAddress();
    
    	/**
    	 * 返回客户端的地址
    	 */
    	@Nullable
    	InetSocketAddress getRemoteAddress();
    
    	/**
    	 *返回约定的子协议,如果没有协议或是协议失败返回null
    	 */
    	@Nullable
    	String getAcceptedProtocol();
    
    	/**
    	 * 配置一次接收文本消息最大值
    	 */
    	void setTextMessageSizeLimit(int messageSizeLimit);
    
    	/**
    	 * 获取一次接收文本消息最大值
    	 */
    	int getTextMessageSizeLimit();
    
    	/**
    	 * 配置一次接收二进制消息最大值
    	 */
    	void setBinaryMessageSizeLimit(int messageSizeLimit);
    
    	/**
    	 * 获取一次接收二进制消息最大值
    	 */
    	int getBinaryMessageSizeLimit();
    
    	/**
    	 * 获取约定的扩展
    	 */
    	List<WebSocketExtension> getExtensions();
    
    	/**
    	 * 发送消息,WebSocket会话底层协议不支持并发发送消息,因此发送必须是同步的。
    	 * 保证信息发送同步进行,一种方法是使用org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator
    	 * 包装WebSocketSession
    	 */
    	void sendMessage(WebSocketMessage<?> message) throws IOException;
    
    	/**
    	 * 底层连接是否打开
    	 */
    	boolean isOpen();
    
    	/**
    	 * 使用状态码1000关闭WebSocket连接
    	 */
    	@Override
    	void close() throws IOException;
    
    	/**
    	 * 使用指定状态码WebSocket连接
    	 */
    	void close(CloseStatus status) throws IOException;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
  • 相关阅读:
    h5开发网站-css实现页面的背景固定定位
    【ASP.NET Core】选项模式的相关接口
    【机器学习】重塑汽车设计与制造:实例与代码探索
    安卓Frida 脱壳
    单节点大数据平台运维脚本
    基于ASP.NET Core 6.0的整洁架构,asp.net core 6.0 功能
    windows操作系统通过浏览器调用本地程序
    IEEE Standard for SystemVerilog Chapter9. Processes
    KubeSphere 社区双周报 | 2022-10-28
    vc中调用matlab生成的动态库
  • 原文地址:https://blog.csdn.net/sanjun333/article/details/125530023