(window.webpackJsonp=window.webpackJsonp||[]).push([[503],{932:function(e,t,n){"use strict";n.r(t);var o=n(56),v=Object(o.a)({},(function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[n("h1",{attrs:{id:"websockets-支持"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websockets-支持"}},[e._v("#")]),e._v(" WebSockets 支持")]),e._v(" "),n("h2",{attrs:{id:"websockets-支持-2"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websockets-支持-2"}},[e._v("#")]),e._v(" WebSockets 支持")]),e._v(" "),n("p",[e._v("从版本 4.1 开始, Spring 集成有 WebSocket 支持。它基于 Spring 框架的"),n("code",[e._v("web-socket")]),e._v("模块中的体系结构、基础设施和 API。因此, Spring WebSocket 的许多组件(例如"),n("code",[e._v("SubProtocolHandler")]),e._v("或"),n("code",[e._v("WebSocketClient")]),e._v(")和配置选项(例如"),n("code",[e._v("@EnableWebSocketMessageBroker")]),e._v(")可以在 Spring 集成内重用。有关更多信息,请参见 Spring 框架参考手册中的"),n("a",{attrs:{href:"https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring Framework WebSocket Support"),n("OutboundLink")],1),e._v("章节。")]),e._v(" "),n("p",[e._v("你需要在项目中包含此依赖项:")]),e._v(" "),n("p",[e._v("Maven")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v("\n org.springframework.integration\n spring-integration-websocket\n 5.5.9\n\n")])])]),n("p",[e._v("Gradle")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('compile "org.springframework.integration:spring-integration-websocket:5.5.9"\n')])])]),n("p",[e._v("对于服务器端,必须显式地包含"),n("code",[e._v("org.springframework:spring-webmvc")]),e._v("依赖项。")]),e._v(" "),n("p",[e._v("Spring 框架 WebSocket 基础设施是基于 Spring 消息传递基础并且提供了基于与 Spring 集成使用的相同的"),n("code",[e._v("MessageChannel")]),e._v("实现和"),n("code",[e._v("MessageHandler")]),e._v("实现(以及一些 POJO-method 注释映射)的基本消息传递框架。因此, Spring 集成可以直接参与到 WebSocket 流中,即使没有 WebSocket 适配器也是如此。为此,你可以使用适当的注释来配置 Spring 集成"),n("code",[e._v("@MessagingGateway")]),e._v(",如下例所示:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@MessagingGateway\n@Controller\npublic interface WebSocketGateway {\n\n @MessageMapping("/greeting")\n @SendToUser("/queue/answer")\n @Gateway(requestChannel = "greetingChannel")\n String greeting(String payload);\n\n}\n')])])]),n("h3",{attrs:{id:"概述"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#概述"}},[e._v("#")]),e._v(" 概述")]),e._v(" "),n("p",[e._v("由于 WebSocket 协议根据定义是流的并且我们可以同时向 WebSocket 发送和接收消息,因此我们可以处理适当的"),n("code",[e._v("WebSocketSession")]),e._v(",而不管是在客户端还是服务器端。为了封装连接管理和"),n("code",[e._v("WebSocketSession")]),e._v("注册中心,"),n("code",[e._v("IntegrationWebSocketContainer")]),e._v("提供了"),n("code",[e._v("ClientWebSocketContainer")]),e._v("和"),n("code",[e._v("ServerWebSocketContainer")]),e._v("实现。由于"),n("a",{attrs:{href:"https://www.jcp.org/en/jsr/detail?id=356",target:"_blank",rel:"noopener noreferrer"}},[e._v("WebSocket API"),n("OutboundLink")],1),e._v("及其在 Spring 框架中的实现(具有许多扩展),在服务器端和客户端都使用了相同的类(当然,从 Java 的角度来看)。因此,大多数连接和"),n("code",[e._v("WebSocketSession")]),e._v("注册表选项在两边都是相同的。这使我们能够重用许多配置项和基础设施挂钩,以便在服务器端和客户端构建 WebSocket 应用程序。下面的示例展示了组件如何实现这两个目的:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('//Client side\n@Bean\npublic WebSocketClient webSocketClient() {\n return new SockJsClient(Collections.singletonList(new WebSocketTransport(new JettyWebSocketClient())));\n}\n\n@Bean\npublic IntegrationWebSocketContainer clientWebSocketContainer() {\n return new ClientWebSocketContainer(webSocketClient(), "ws://my.server.com/endpoint");\n}\n\n//Server side\n@Bean\npublic IntegrationWebSocketContainer serverWebSocketContainer() {\n return new ServerWebSocketContainer("/endpoint").withSockJs();\n}\n')])])]),n("p",[e._v("WebSocket 该被设计用于实现双向消息传递,并且可以在入站和出站通道适配器之间共享(见下文),在使用单向消息传递时可以仅从其中一个引用 WebSocket。它可以在没有任何通道适配器的情况下使用,但是,在这种情况下,"),n("code",[e._v("IntegrationWebSocketContainer")]),e._v("仅作为"),n("code",[e._v("WebSocketSession")]),e._v("注册中心发挥作用。")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[n("code",[e._v("ServerWebSocketContainer")]),e._v("实现"),n("code",[e._v("WebSocketConfigurer")]),e._v("将一个内部"),n("code",[e._v("IntegrationWebSocketContainer.IntegrationWebSocketHandler")]),e._v("注册为"),n("code",[e._v("Endpoint")]),e._v("。"),n("br"),e._v("它在提供的"),n("code",[e._v("paths")]),e._v("下这样做和其他服务器 WebSocket 选项(例如"),n("code",[e._v("HandshakeHandler")]),e._v("或"),n("code",[e._v("SockJS fallback")]),e._v(")内的"),n("code",[e._v("ServletWebSocketHandlerRegistry")]),e._v("用于目标供应商 WebSocket 容器。"),n("br"),e._v("该注册是通过一个基础设施"),n("code",[e._v("WebSocketIntegrationConfigurationInitializer")]),e._v("组件实现的,其执行与"),n("code",[e._v("@EnableWebSocket")]),e._v("注释相同的操作。"),n("br"),e._v("这意味着,通过使用"),n("code",[e._v("@EnableIntegration")]),e._v("(或应用程序上下文中的任何 Spring 集成名称空间),你可以省略"),n("code",[e._v("@EnableWebSocket")]),e._v("声明,因为 Spring 集成基础结构检测所有 WebSocket 端点。")])])]),e._v(" "),n("tbody")]),e._v(" "),n("h3",{attrs:{id:"websocket-入站通道适配器"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websocket-入站通道适配器"}},[e._v("#")]),e._v(" WebSocket 入站通道适配器")]),e._v(" "),n("p",[n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v("实现了"),n("code",[e._v("WebSocketSession")]),e._v("交互的接收部分。你必须为它提供"),n("code",[e._v("IntegrationWebSocketContainer")]),e._v(",并且适配器将自身注册为"),n("code",[e._v("WebSocketListener")]),e._v("以处理传入消息和"),n("code",[e._v("WebSocketSession")]),e._v("事件。")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("只有一个"),n("code",[e._v("WebSocketListener")]),e._v("可以在"),n("code",[e._v("IntegrationWebSocketContainer")]),e._v("中注册。")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("对于 WebSocket 子协议,"),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v("可以配置"),n("code",[e._v("SubProtocolHandlerRegistry")]),e._v("作为第二个构造函数参数。适配器委托给"),n("code",[e._v("SubProtocolHandlerRegistry")]),e._v(",以确定适用于已接受的"),n("code",[e._v("WebSocketSession")]),e._v("的"),n("code",[e._v("SubProtocolHandler")]),e._v(",并根据子协议实现将"),n("code",[e._v("WebSocketMessage")]),e._v("转换为"),n("code",[e._v("Message")]),e._v("。")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("默认情况下,"),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v("仅依赖于 RAW"),n("code",[e._v("PassThruSubProtocolHandler")]),e._v("实现,该实现将"),n("code",[e._v("WebSocketMessage")]),e._v("转换为"),n("code",[e._v("Message")]),e._v("。")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v("只接受并发送到底层集成流"),n("code",[e._v("Message")]),e._v("实例,这些实例具有"),n("code",[e._v("SimpMessageType.MESSAGE")]),e._v("或空的"),n("code",[e._v("simpMessageType")]),e._v("头。所有其他"),n("code",[e._v("Message")]),e._v("类型都是通过"),n("code",[e._v("ApplicationEvent")]),e._v("实现发出的"),n("code",[e._v("SubProtocolHandler")]),e._v("实例来处理的(例如"),n("code",[e._v("StompSubProtocolHandler")]),e._v(")。")]),e._v(" "),n("p",[e._v("在服务器端,如果存在"),n("code",[e._v("@EnableWebSocketMessageBroker")]),e._v("配置,则可以使用"),n("code",[e._v("useBroker = true")]),e._v("选项配置"),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v("。在这种情况下,所有"),n("code",[e._v("non-MESSAGE``Message")]),e._v("类型都被委托给所提供的"),n("code",[e._v("AbstractBrokerMessageHandler")]),e._v("。此外,如果代理中继配置了目标前缀,那么那些匹配代理目标的消息将路由到"),n("code",[e._v("AbstractBrokerMessageHandler")]),e._v(",而不是"),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v("的"),n("code",[e._v("outputChannel")]),e._v("。")]),e._v(" "),n("p",[e._v("如果"),n("code",[e._v("useBroker = false")]),e._v("并且接收到的消息是"),n("code",[e._v("SimpMessageType.CONNECT")]),e._v("类型的,则"),n("code",[e._v("WebSocketInboundChannelAdapter")]),e._v("立即向"),n("code",[e._v("SimpMessageType.CONNECT_ACK")]),e._v("消息发送"),n("code",[e._v("WebSocketSession")]),e._v(",而不将其发送到信道。")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("Spring 的 WebSocket 支持只允许配置一个代理中继。"),n("br"),e._v("因此,我们不需要"),n("code",[e._v("AbstractBrokerMessageHandler")]),e._v("引用。"),n("br"),e._v("它是在应用程序上下文中检测到的。")])])]),e._v(" "),n("tbody")]),e._v(" "),n("p",[e._v("有关更多配置选项,请参见"),n("a",{attrs:{href:"#web-sockets-namespace"}},[e._v("WebSockets 名称空间支持")]),e._v("。")]),e._v(" "),n("h3",{attrs:{id:"websocket-出站通道适配器"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websocket-出站通道适配器"}},[e._v("#")]),e._v(" WebSocket 出站通道适配器")]),e._v(" "),n("p",[e._v("the"),n("code",[e._v("WebSocketOutboundChannelAdapter")]),e._v(":")]),e._v(" "),n("ol",[n("li",[n("p",[e._v("接受 Spring 来自其"),n("code",[e._v("MessageChannel")]),e._v("的集成消息")])]),e._v(" "),n("li",[n("p",[e._v("从"),n("code",[e._v("MessageHeaders")]),e._v("中确定"),n("code",[e._v("WebSocketSession``id")])])]),e._v(" "),n("li",[n("p",[e._v("从提供的"),n("code",[e._v("IntegrationWebSocketContainer")]),e._v("检索"),n("code",[e._v("WebSocketSession")])])]),e._v(" "),n("li",[n("p",[e._v("从提供的"),n("code",[e._v("SubProtocolHandlerRegistry")]),e._v("将"),n("code",[e._v("WebSocketMessage")]),e._v("工作的转换和发送委托给相应的"),n("code",[e._v("SubProtocolHandler")]),e._v("。")])])]),e._v(" "),n("p",[e._v("在客户端,"),n("code",[e._v("WebSocketSession``id")]),e._v("消息头不是必需的,因为"),n("code",[e._v("ClientWebSocketContainer")]),e._v("仅处理单个连接及其"),n("code",[e._v("WebSocketSession")]),e._v("分别。")]),e._v(" "),n("p",[e._v("要使用 STOMP 子协议,你应该使用"),n("code",[e._v("StompSubProtocolHandler")]),e._v("配置此适配器。然后,你可以使用"),n("code",[e._v("StompHeaderAccessor.create(StompCommand…​)")]),e._v("和"),n("code",[e._v("MessageBuilder")]),e._v("向此适配器发送任何 Stomp 消息类型,或者只使用"),n("code",[e._v("HeaderEnricher")]),e._v("(参见"),n("RouterLink",{attrs:{to:"/spring-integration/content-enrichment.html#header-enricher"}},[e._v("页眉 Enricher")]),e._v(")。")],1),e._v(" "),n("p",[e._v("本章的其余部分主要介绍了附加的配置选项。")]),e._v(" "),n("h3",{attrs:{id:"websockets-名称空间支持"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#websockets-名称空间支持"}},[e._v("#")]),e._v(" WebSockets 名称空间支持")]),e._v(" "),n("p",[e._v("Spring 集成 WebSocket 命名空间包括在本章的其余部分中描述的几个组件。要将其包含在配置中,请在应用程序上下文配置文件中使用以下名称空间声明:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('\n\n ...\n\n')])])]),n("h4",{attrs:{id:"int-websocket-client-container-属性"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#int-websocket-client-container-属性"}},[e._v("#")]),e._v(" "),n("code",[e._v("")]),e._v("属性")]),e._v(" "),n("p",[e._v("下面的清单显示了"),n("code",[e._v("")]),e._v("元素可用的属性:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v(' (9)\n \n \n (10)\n\n')])])]),n("table",[n("thead",[n("tr",[n("th",[n("strong",[e._v("1")])]),e._v(" "),n("th",[e._v("组件 Bean 名称。")])])]),e._v(" "),n("tbody",[n("tr",[n("td",[n("strong",[e._v("2")])]),e._v(" "),n("td",[e._v("参考文献"),n("code",[e._v("WebSocketClient")]),e._v(" Bean。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("3")])]),e._v(" "),n("td",[e._v("将"),n("code",[e._v("uri")]),e._v("或"),n("code",[e._v("uriTemplate")]),e._v("发送到目标 WebSocket 服务。"),n("br"),e._v("如果将其用作带有 URI 变量占位符的"),n("code",[e._v("uriTemplate")]),e._v(",则需要"),n("code",[e._v("uri-variables")]),e._v("属性。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("4")])]),e._v(" "),n("td",[n("br"),e._v("属性值内的 URI 变量占位符的逗号分隔的值。"),n("br"),e._v("这些值根据它们在"),n("code",[e._v("uri")]),e._v("中的顺序被替换到占位符中。"),n("br"),e._v("参见["),n("code",[e._v("UriComponents.expand(Object…​uriVariableValues)")]),e._v("(https://DOCS. Spring.io/ Spring//DOCS/current/javadoc-api/org/SpringFramework/web/util/utilComponents.html expand-uribot)。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("5")])]),e._v(" "),n("td",[n("code",[e._v("Origin")]),e._v("握手 HTTP 标头值。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("6")])]),e._v(" "),n("td",[e._v("WebSocket 会话“发送”超时限制。"),n("br"),e._v("默认为"),n("code",[e._v("10000")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("7")])]),e._v(" "),n("td",[e._v("WebSocket 会话“发送”消息大小限制。"),n("br"),e._v("默认为"),n("code",[e._v("524288")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("8")])]),e._v(" "),n("td",[e._v("布尔值,表示此端点是否应自动启动。"),n("br"),e._v("默认为"),n("code",[e._v("false")]),e._v(",假设此容器是从"),n("a",{attrs:{href:"#web-socket-inbound-adapter"}},[e._v("WebSocket inbound adapter")]),e._v("启动的。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("9")])]),e._v(" "),n("td",[e._v("这个端点应该在其中开始和停止的生命周期阶段。"),n("br"),e._v("值越低,这个端点开始得越早,停止得越晚。"),n("br"),e._v("默认值是"),n("code",[e._v("Integer.MAX_VALUE")]),e._v("。"),n("br"),e._v("值可以是负数。"),n("br"),e._v("参见["),n("code",[e._v("SmartLifeCycle")]),e._v("(https:/DOCS. Spring.io/ Spring//DOCS/current/javadoc-api/org/api/context/smartlifycle.html)。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("10")])]),e._v(" "),n("td",[e._v("a"),n("code",[e._v("Map")]),e._v("of"),n("code",[e._v("HttpHeaders")]),e._v("用于握手请求。")])])])]),e._v(" "),n("h4",{attrs:{id:"int-websocket-server-container-属性"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#int-websocket-server-container-属性"}},[e._v("#")]),e._v(" "),n("code",[e._v("")]),e._v("属性")]),e._v(" "),n("p",[e._v("下面的清单显示了"),n("code",[e._v("")]),e._v("元素可用的属性:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v(' (8)\n (19)\n\n')])])]),n("table",[n("thead",[n("tr",[n("th",[n("strong",[e._v("1")])]),e._v(" "),n("th",[e._v("组件 Bean 名称。")])])]),e._v(" "),n("tbody",[n("tr",[n("td",[n("strong",[e._v("2")])]),e._v(" "),n("td",[e._v("一种将特定请求映射到"),n("code",[e._v("WebSocketHandler")]),e._v("的路径(或以逗号分隔的路径)。"),n("br"),e._v("支持精确的路径映射 URI(例如"),n("code",[e._v("/myPath")]),e._v(")和 Ant 样式的路径模式(例如"),n("code",[e._v("/myPath/**")]),e._v(")。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("3")])]),e._v(" "),n("td",[n("code",[e._v("HandshakeHandler")]),e._v(" Bean 引用."),n("br"),e._v("默认为"),n("code",[e._v("DefaultHandshakeHandler")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("4")])]),e._v(" "),n("td",[e._v("参考文献列表"),n("code",[e._v("HandshakeInterceptor")]),e._v(" Bean。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("5")])]),e._v(" "),n("td",[e._v("用于修饰用于处理 WebSocket 消息的处理程序的一个或多个工厂的列表("),n("code",[e._v("WebSocketHandlerDecoratorFactory")]),e._v(")。"),n("br"),e._v("这对于某些高级用例(例如,以允许 Spring 安全性在相应的 HTTP会话过期时强制关闭 WebSocket 会话。有关更多信息,请参见。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("6")])]),e._v(" "),n("td",[e._v("请参阅["),n("code",[e._v("")]),e._v("](# WebSocket-client-container-attributes)上的相同选项。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("7")])]),e._v(" "),n("td",[e._v("请参阅["),n("code",[e._v("")]),e._v("](# WebSocket-client-container-attributes)上的相同选项。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("8")])]),e._v(" "),n("td",[e._v("允许的源头值。"),n("br"),e._v("你可以将多个源代码指定为逗号分隔的列表。"),n("br"),e._v("此检查主要是为浏览器客户端设计的。"),n("br"),e._v("没有什么可以阻止其他类型的客户端修改源头值。"),n("br"),e._v("当启用 Sockjs 并且限制允许的源代码时,不使用起源头来处理跨起源请求("),n("code",[e._v("jsonp-polling")]),e._v(","),n("code",[e._v("iframe-xhr-polling")]),e._v(","),n("code",[e._v("iframe-eventsource")]),e._v(",和"),n("code",[e._v("iframe-htmlfile")]),e._v(")的传输类型将被禁用。因此,不支持 IE6 和 IE7,并且只支持 IE8 和 IE9 而不支持 cookie。"),n("br"),e._v("默认情况下,所有起源都是允许的。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("9")])]),e._v(" "),n("td",[e._v("没有本机跨域通信的传输(例如"),n("code",[e._v("eventsource")]),e._v("和"),n("code",[e._v("htmlfile")]),e._v(")必须在不可见的 iframe 中从“foreign”域获得一个简单的页面,以便 IFRAME 中的代码可以从本地域运行到 Sockjs 服务器。"),n("br"),e._v("由于 IFRAME 需要加载 Sockjs JavaScript 客户库,因此该属性允许你指定加载它的位置。"),n("br"),e._v("默认情况下,它指向"),n("code",[e._v("[https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js](https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js)")]),e._v("。,但是,"),n("br"),e._v(",还可以将它设置为指向应用程序提供的 URL。"),n("br"),e._v("注意,可以指定一个相对 URL,在这种情况下,URL 必须与 IFRAME URL 相对。"),n("br"),e._v(",例如,假设一个 Sockjs 端点映射到"),n("code",[e._v("/sockjs")]),e._v(",并且得到的 iFrame URL 是"),n("code",[e._v("/sockjs/iframe.html")]),e._v(",那么相对 URL 必须以“.././”开头,才能遍历到 Sockjs 映射上方的位置。"),n("br"),e._v("对于基于前缀的 Servlet 映射,你可能需要再遍历一次。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("10")])]),e._v(" "),n("td",[e._v("在关闭单个 HTTP 流请求之前,可以通过该请求发送的最小字节数。"),n("br"),e._v("默认为"),n("code",[e._v("128K")]),e._v("(即 128*1024 或 131072 字节)。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("11")])]),e._v(" "),n("td",[e._v("来自 Sockjs 的响应中的"),n("code",[e._v("cookie_needed")]),e._v("值"),n("code",[e._v("/info")]),e._v("Endpoint."),n("br"),e._v("此属性指示应用程序是否需要"),n("code",[e._v("JSESSIONID")]),e._v("cookie 才能正常工作(例如,用于负载平衡或在 Java Servlet 容器中使用 HTTP会话)。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("12")])]),e._v(" "),n("td",[e._v("当服务器没有发送任何消息时的时间量(以毫秒为单位),在此之后服务器应该"),n("br"),e._v("向客户端发送心跳帧,以防止连接中断。"),n("br"),e._v("默认值为"),n("code",[e._v("25,000")]),e._v("(25 秒)。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("13")])]),e._v(" "),n("td",[e._v("客户机在没有接收连接(即服务器可以在其上向客户机发送数据的活动连接)之后被认为断开连接之前的时间量(以毫秒为单位)。"),n("br"),e._v("默认值为"),n("code",[e._v("5000")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("14")])]),e._v(" "),n("td",[e._v("会话在等待来自客户端的下一个 HTTP 轮询请求时可以缓存的服务器到客户端消息的数量。"),n("br"),e._v("默认大小为"),n("code",[e._v("100")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("15")])]),e._v(" "),n("td",[e._v("有些负载均衡器不支持 WebSockets。"),n("br"),e._v("将此选项设置为"),n("code",[e._v("false")]),e._v(",以禁用服务器端的 WebSocket 传输。"),n("br"),e._v("默认值为"),n("code",[e._v("true")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("16")])]),e._v(" "),n("td",[n("code",[e._v("TaskScheduler")]),e._v(" Bean 引用。"),n("br"),e._v("如果不提供值,将创建一个新的"),n("code",[e._v("ThreadPoolTaskScheduler")]),e._v("实例。"),n("br"),e._v("此调度程序实例用于调度心跳消息。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("17")])]),e._v(" "),n("td",[e._v("该 Bean 引用用于编码和解码 Sockjs 消息。默认情况下,使用,这要求在 Classpath 上存在 Jackson 库。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("18")])]),e._v(" "),n("td",[e._v("参考文献列表"),n("code",[e._v("TransportHandler")]),e._v(" Bean。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("19")])]),e._v(" "),n("td",[e._v("是否禁用自动添加 SOCKJS 请求的 CORS 头。"),n("br"),e._v("默认值为"),n("code",[e._v("false")]),e._v("。")])])])]),e._v(" "),n("h4",{attrs:{id:"int-websocket-outbound-channel-adapter-属性"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#int-websocket-outbound-channel-adapter-属性"}},[e._v("#")]),e._v(" "),n("code",[e._v("")]),e._v("属性")]),e._v(" "),n("p",[e._v("下面的清单显示了"),n("code",[e._v("")]),e._v("元素可用的属性:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v(' (9)\n')])])]),n("table",[n("thead",[n("tr",[n("th",[n("strong",[e._v("1")])]),e._v(" "),n("th",[e._v("组件 Bean name."),n("br"),e._v("如果不提供"),n("code",[e._v("channel")]),e._v("属性,则在应用程序上下文中创建并注册一个"),n("code",[e._v("DirectChannel")]),e._v(",并将该"),n("code",[e._v("id")]),e._v("属性作为 Bean 名称。"),n("br"),e._v("在这种情况下,端点以 Bean 名称"),n("code",[e._v("id")]),e._v("加上"),n("code",[e._v(".adapter")]),e._v("注册。"),n("br"),e._v("而"),n("code",[e._v("MessageHandler")]),e._v("则以 Bean 别名"),n("code",[e._v("id")]),e._v("加上"),n("code",[e._v(".handler")]),e._v("注册。")])])]),e._v(" "),n("tbody",[n("tr",[n("td",[n("strong",[e._v("2")])]),e._v(" "),n("td",[e._v("标识连接到此适配器的通道。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("3")])]),e._v(" "),n("td",[e._v("对"),n("code",[e._v("IntegrationWebSocketContainer")]),e._v(" Bean 的引用,它封装了低级连接和"),n("code",[e._v("WebSocketSession")]),e._v("处理操作。"),n("br"),e._v("需要。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("4")])]),e._v(" "),n("td",[e._v("对"),n("code",[e._v("SubProtocolHandler")]),e._v("实例的可选引用。"),n("br"),e._v("当客户端未请求子协议或它是单个协议处理程序时使用。"),n("br"),e._v("如果未提供此引用或"),n("code",[e._v("protocol-handlers")]),e._v("列表,则默认使用"),n("code",[e._v("PassThruSubProtocolHandler")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("5")])]),e._v(" "),n("td",[e._v("该通道适配器的"),n("code",[e._v("SubProtocolHandler")]),e._v(" Bean 引用列表。"),n("br"),e._v("如果只提供单个 Bean 引用而不提供"),n("code",[e._v("default-protocol-handler")]),e._v(",则该单个"),n("code",[e._v("SubProtocolHandler")]),e._v("用作"),n("code",[e._v("default-protocol-handler")]),e._v("。"),n("br"),e._v("如果不设置此属性或"),n("code",[e._v("default-protocol-handler")]),e._v(",默认情况下使用"),n("code",[e._v("PassThruSubProtocolHandler")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("6")])]),e._v(" "),n("td",[e._v("此通道适配器的"),n("code",[e._v("MessageConverter")]),e._v(" Bean 引用列表。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("7")])]),e._v(" "),n("td",[e._v("布尔值,表示是否应在任何自定义转换器之后注册默认转换器。"),n("br"),e._v("只有在提供"),n("code",[e._v("message-converters")]),e._v("时才使用此标志。"),n("br"),e._v("否则,将注册所有默认转换器。"),n("br"),e._v("默认为"),n("code",[e._v("false")]),e._v("。"),n("br"),e._v("默认转换器(按顺序排列):"),n("code",[e._v("StringMessageConverter")]),e._v(","),n("code",[e._v("ByteArrayMessageConverter")]),e._v(",和(如果 Jackson 库存在于 Classpath 上)。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("8")])]),e._v(" "),n("td",[e._v("布尔值,表示此端点是否应自动启动。"),n("br"),e._v("默认为"),n("code",[e._v("true")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("9")])]),e._v(" "),n("td",[e._v("这个端点应该在其中开始和停止的生命周期阶段。"),n("br"),e._v("值越低,这个端点开始得越早,停止得越晚。"),n("br"),e._v("默认值是"),n("code",[e._v("Integer.MIN_VALUE")]),e._v("。"),n("br"),e._v("值可以是负数。"),n("br"),e._v("参见["),n("code",[e._v("SmartLifeCycle")]),e._v("(https:/DOCS. Spring.io/ Spring/DOCS/current/javadoc-apf/org/api/context/smartlifycle.html)。")])])])]),e._v(" "),n("h4",{attrs:{id:"int-websocket-inbound-channel-adapter-属性"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#int-websocket-inbound-channel-adapter-属性"}},[e._v("#")]),e._v(" "),n("code",[e._v("")]),e._v("属性")]),e._v(" "),n("p",[e._v("下面的清单显示了"),n("code",[e._v("")]),e._v("元素可用的属性:")]),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v(' (13)\n')])])]),n("table",[n("thead",[n("tr",[n("th",[n("strong",[e._v("1")])]),e._v(" "),n("th",[e._v("组件 Bean name."),n("br"),e._v("如果不设置"),n("code",[e._v("channel")]),e._v("属性,则在应用程序上下文中创建并注册一个"),n("code",[e._v("DirectChannel")]),e._v(",并将该"),n("code",[e._v("id")]),e._v("属性作为 Bean 名称。"),n("br"),e._v("在本例中,端点以 Bean 名称"),n("code",[e._v("id")]),e._v("加上"),n("code",[e._v(".adapter")]),e._v("进行注册。")])])]),e._v(" "),n("tbody",[n("tr",[n("td",[n("strong",[e._v("2")])]),e._v(" "),n("td",[e._v("标识连接到此适配器的通道。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("3")])]),e._v(" "),n("td",[e._v("应该发送"),n("code",[e._v("ErrorMessage")]),e._v("实例的"),n("code",[e._v("MessageChannel")]),e._v(" Bean 引用。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("4")])]),e._v(" "),n("td",[e._v("请参阅["),n("code",[e._v("")]),e._v("](# WebSocket-出站-通道-适配器-属性)上的相同选项。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("5")])]),e._v(" "),n("td",[e._v("请参阅["),n("code",[e._v("")]),e._v("](# WebSocket-出站-通道-适配器-属性)上的相同选项。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("6")])]),e._v(" "),n("td",[e._v("请参阅["),n("code",[e._v("")]),e._v("](# WebSocket-出站-通道-适配器-属性)上的相同选项。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("7")])]),e._v(" "),n("td",[e._v("请参阅["),n("code",[e._v("")]),e._v("](# WebSocket-出站-通道-适配器-属性)上的相同选项。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("8")])]),e._v(" "),n("td",[e._v("请参阅["),n("code",[e._v("")]),e._v("](# WebSocket-出站-通道-适配器-属性)上的相同选项。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("9")])]),e._v(" "),n("td",[e._v("如果信道可以阻塞,则在向信道发送消息时等待的最大时间量(以毫秒为单位)。"),n("br"),e._v("例如,如果已达到其最大容量,则"),n("code",[e._v("QueueChannel")]),e._v("可以阻塞直到可用空间。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("10")])]),e._v(" "),n("td",[e._v("用于从传入的"),n("code",[e._v("WebSocketMessage")]),e._v("转换为目标"),n("code",[e._v("payload")]),e._v("的完全限定的 Java 类型名称。"),n("br"),e._v("默认为"),n("code",[e._v("java.lang.String")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("11")])]),e._v(" "),n("td",[e._v("指示此适配器是否从应用程序上下文向"),n("code",[e._v("AbstractBrokerMessageHandler")]),e._v("发送带有代理目的地的"),n("code",[e._v("AbstractBrokerMessageHandler")]),e._v("实例和消息。"),n("br"),e._v("当此属性为"),n("code",[e._v("true")]),e._v("时,需要"),n("code",[e._v("Broker Relay")]),e._v("配置。"),n("br"),e._v("此属性仅在服务器端使用。"),n("br"),e._v("在客户端,它被忽略。"),n("br"),e._v("默认为"),n("code",[e._v("false")]),e._v("。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("12")])]),e._v(" "),n("td",[e._v("请参阅["),n("code",[e._v("")]),e._v("](# WebSocket-出站-通道-适配器-属性)上的相同选项。")])]),e._v(" "),n("tr",[n("td",[n("strong",[e._v("13")])]),e._v(" "),n("td",[e._v("参见["),n("code",[e._v("")]),e._v("](# WebSocket-出站-通道-适配器-属性)上的相同选项。")])])])]),e._v(" "),n("h3",{attrs:{id:"使用clientstompencoder"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#使用clientstompencoder"}},[e._v("#")]),e._v(" 使用"),n("code",[e._v("ClientStompEncoder")])]),e._v(" "),n("p",[e._v("从版本 4.3.13 开始, Spring 集成提供了"),n("code",[e._v("ClientStompEncoder")]),e._v("(作为标准"),n("code",[e._v("StompEncoder")]),e._v("的扩展),用于在 WebSocket 通道适配器的客户端上使用。为了进行正确的客户端消息准备,你必须将"),n("code",[e._v("ClientStompEncoder")]),e._v("的一个实例注入"),n("code",[e._v("StompSubProtocolHandler")]),e._v("中。默认"),n("code",[e._v("StompSubProtocolHandler")]),e._v("的一个问题是,它是为服务器端设计的,因此它将"),n("code",[e._v("SEND``stompCommand")]),e._v("报头更新为"),n("code",[e._v("MESSAGE")]),e._v("(根据服务器端的 stomp 协议的要求)。如果客户机没有在适当的"),n("code",[e._v("SEND")]),e._v("Web 套接字框架中发送消息,则某些 STOMP 代理不接受它们。在这种情况下,"),n("code",[e._v("ClientStompEncoder")]),e._v("的目的是覆盖"),n("code",[e._v("stompCommand")]),e._v("头并将其设置为"),n("code",[e._v("SEND")]),e._v("值,然后将消息编码为"),n("code",[e._v("byte[]")]),e._v("。")]),e._v(" "),n("h3",{attrs:{id:"动态-websocket-端点注册"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#动态-websocket-端点注册"}},[e._v("#")]),e._v(" 动态 WebSocket 端点注册")]),e._v(" "),n("p",[e._v("从版本 5.5 开始, WebSocket 服务器端点(基于"),n("code",[e._v("ServerWebSocketContainer")]),e._v("的通道适配器)现在可以在运行时注册(并删除)-映射的"),n("code",[e._v("paths")]),e._v("a"),n("code",[e._v("ServerWebSocketContainer")]),e._v("通过"),n("code",[e._v("HandlerMapping")]),e._v("公开到"),n("code",[e._v("DispatcherServlet")]),e._v("中,并可供 WebSocket 客户端访问。"),n("RouterLink",{attrs:{to:"/spring-integration/dsl.html#java-dsl-runtime-flows"}},[e._v("动态和运行时集成流")]),e._v("支持有助于以透明的方式注册这些端点:")],1),e._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[e._v('@Autowired\nIntegrationFlowContext integrationFlowContext;\n\n@Autowired\nHandshakeHandler handshakeHandler;\n...\nServerWebSocketContainer serverWebSocketContainer =\n new ServerWebSocketContainer("/dynamic")\n .setHandshakeHandler(this.handshakeHandler);\n\nWebSocketInboundChannelAdapter webSocketInboundChannelAdapter =\n new WebSocketInboundChannelAdapter(serverWebSocketContainer);\n\nQueueChannel dynamicRequestsChannel = new QueueChannel();\n\nIntegrationFlow serverFlow =\n IntegrationFlows.from(webSocketInboundChannelAdapter)\n .channel(dynamicRequestsChannel)\n .get();\n\nIntegrationFlowContext.IntegrationFlowRegistration dynamicServerFlow =\n this.integrationFlowContext.registration(serverFlow)\n .addBean(serverWebSocketContainer)\n .register();\n...\ndynamicServerFlow.destroy();\n')])])]),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("在动态流注册中调用"),n("code",[e._v(".addBean(serverWebSocketContainer)")]),e._v("将"),n("code",[e._v("ServerWebSocketContainer")]),e._v("的实例添加到"),n("code",[e._v("ApplicationContext")]),e._v("中以进行端点注册是很重要的,"),n("br"),e._v("当动态流注册被破坏时,相关的"),n("code",[e._v("ServerWebSocketContainer")]),e._v("实例也被破坏,以及相应的端点注册也被破坏,包括 URL 路径映射。")])])]),e._v(" "),n("tbody")]),e._v(" "),n("table",[n("thead",[n("tr",[n("th"),e._v(" "),n("th",[e._v("动态 WebSocket 端点只能通过 Spring 集成机制进行注册:当使用常规 Spring 时, Spring 集成配置退出,并且不注册用于动态端点的基础设施。")])])]),e._v(" "),n("tbody")])])}),[],!1,null,null,null);t.default=v.exports}}]);