(window.webpackJsonp=window.webpackJsonp||[]).push([[508],{941:function(e,t,a){"use strict";a.r(t);var n=a(56),r=Object(n.a)({},(function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[a("h1",{attrs:{id:"xmpp-支持"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#xmpp-支持"}},[e._v("#")]),e._v(" XMPP 支持")]),e._v(" "),a("h2",{attrs:{id:"xmpp-支持-2"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#xmpp-支持-2"}},[e._v("#")]),e._v(" XMPP 支持")]),e._v(" "),a("p",[e._v("Spring 集成为"),a("a",{attrs:{href:"https://www.xmpp.org",target:"_blank",rel:"noopener noreferrer"}},[e._v("XMPP"),a("OutboundLink")],1),e._v("提供通道适配器。XMPP 代表“可扩展消息传递和存在协议”。")]),e._v(" "),a("p",[e._v("XMPP 描述了在分布式系统中多个代理相互通信的一种方式。典型的用例是发送和接收聊天消息,尽管 XMPP 可以(并且正在)用于其他类型的应用程序。XMPP 描述了参与者的网络。在该网络中,演员可以直接相互联系,并改变广播状态(例如“存在”)。")]),e._v(" "),a("p",[e._v("XMPP 提供的消息架构是世界上一些最大的即时消息网络的基础,包括 Google Talk(Gtalk,也可在 Gmail 中使用)和 Facebook Chat。许多好的开源 XMPP 服务器都是可用的。两种流行的实现方式是"),a("a",{attrs:{href:"https://www.igniterealtime.org/projects/openfire/",target:"_blank",rel:"noopener noreferrer"}},[e._v("Openfire"),a("OutboundLink")],1),e._v("和"),a("a",{attrs:{href:"https://www.ejabberd.im",target:"_blank",rel:"noopener noreferrer"}},[e._v("ejabberd"),a("OutboundLink")],1),e._v("。")]),e._v(" "),a("p",[e._v("Spring 集成通过提供 XMPP 适配器来提供对 XMPP 的支持,该适配器支持发送和接收 XMPP 聊天消息以及来自客户端花名册中其他条目的存在变化。")]),e._v(" "),a("p",[e._v("你需要在项目中包含此依赖项:")]),e._v(" "),a("p",[e._v("Maven")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("\n org.springframework.integration\n spring-integration-xmpp\n 5.5.9\n\n")])])]),a("p",[e._v("Gradle")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('compile "org.springframework.integration:spring-integration-xmpp:5.5.9"\n')])])]),a("p",[e._v("与其他适配器一样,XMPP 适配器支持方便的基于名称空间的配置。要配置 XMPP 名称空间,在 XML 配置文件的头中包含以下元素:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('xmlns:int-xmpp="http://www.springframework.org/schema/integration/xmpp"\nxsi:schemaLocation="http://www.springframework.org/schema/integration/xmpp\n\thttps://www.springframework.org/schema/integration/xmpp/spring-integration-xmpp.xsd"\n')])])]),a("h3",{attrs:{id:"xmpp-连接"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#xmpp-连接"}},[e._v("#")]),e._v(" XMPP 连接")]),e._v(" "),a("p",[e._v("在使用入站或出站 XMPP 适配器参与 XMPP 网络之前,参与者必须建立其 XMPP 连接。连接到特定帐户的所有 XMPP 适配器都可以共享此连接对象。通常这需要(至少)"),a("code",[e._v("user")]),e._v("、"),a("code",[e._v("password")]),e._v("和"),a("code",[e._v("host")]),e._v("。要创建基本的 XMPP 连接,你可以使用名称空间的便利,如下例所示:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n')])])]),a("table",[a("thead",[a("tr",[a("th"),e._v(" "),a("th",[e._v("为了更方便起见,你可以依赖默认的命名约定,并省略"),a("code",[e._v("id")]),e._v("属性。"),a("br"),e._v("此连接使用的默认名称("),a("code",[e._v("xmppConnection")]),e._v(") Bean。")])])]),e._v(" "),a("tbody")]),e._v(" "),a("p",[e._v("如果 XMPP 连接失效,只要上一个连接状态被记录(经过身份验证),就会使用自动登录进行重新连接尝试。我们还注册了"),a("code",[e._v("ConnectionListener")]),e._v(",如果启用了"),a("code",[e._v("DEBUG")]),e._v("日志级别,它将记录连接事件。")]),e._v(" "),a("p",[a("code",[e._v("subscription-mode")]),e._v("属性启动 Rose Listener 来处理来自其他用户的订阅。此功能并不总是适用于目标 XMPP 服务器。例如,Google Cloud Messaging 和 Firebase Cloud Messaging 完全禁用了它。要关闭订阅的花名册侦听器,你可以在使用 XML 配置("),a("code",[e._v('subscription-mode=""')]),e._v(")时使用空字符串进行配置,或者在使用 Java 配置时使用"),a("code",[e._v("XmppConnectionFactoryBean.setSubscriptionMode(null)")]),e._v("进行配置。这样做也会在登录阶段禁用花名册。有关更多信息,请参见["),a("code",[e._v("Roster.setRosterLoadedAtLogin(boolean)")]),e._v("](https://download.igniterealtime.org/smack/DOCS/latest/javadoc/org/jivesoftware/smack/roster/roster.html#setrosterloadedatlogin-boolean-)。")]),e._v(" "),a("h3",{attrs:{id:"xmpp-消息"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#xmpp-消息"}},[e._v("#")]),e._v(" XMPP 消息")]),e._v(" "),a("p",[e._v("Spring 集成为发送和接收 XMPP 消息提供了支持。为了接收它们,它提供了一个入站消息通道适配器。对于发送它们,它提供了一个出站消息通道适配器。")]),e._v(" "),a("h4",{attrs:{id:"入站消息通道适配器"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#入站消息通道适配器"}},[e._v("#")]),e._v(" 入站消息通道适配器")]),e._v(" "),a("p",[e._v("Spring 集成适配器支持从系统中的其他用户接收聊天消息。为此,入站消息通道适配器代表用户“登录”,并接收发送给该用户的消息。然后将这些消息转发到你的 Spring 集成客户端。"),a("code",[e._v("inbound-channel-adapter")]),e._v("元素为 XMPP 入站消息通道适配器提供配置支持。下面的示例展示了如何配置它:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n')])])]),a("p",[e._v("除了通常的属性(对于消息通道适配器),这个适配器还需要对 XMPP 连接的引用。")]),e._v(" "),a("p",[e._v("XMPP 入站适配器是事件驱动的,并且是"),a("code",[e._v("Lifecycle")]),e._v("实现。启动时,它会注册一个"),a("code",[e._v("PacketListener")]),e._v(",用于侦听传入的 XMPP 聊天消息。它将接收到的任何消息转发给底层适配器,后者将它们转换为 Spring 集成消息,并将它们发送到指定的"),a("code",[e._v("channel")]),e._v("。当停止时,它将注销"),a("code",[e._v("PacketListener")]),e._v("。")]),e._v(" "),a("p",[e._v("从版本 4.3 开始,"),a("code",[e._v("ChatMessageListeningEndpoint")]),e._v("(及其"),a("code",[e._v("")]),e._v(")支持在提供的"),a("code",[e._v("org.jivesoftware.smack.filter.StanzaFilter")]),e._v("上注册一个"),a("code",[e._v("org.jivesoftware.smack.filter.StanzaFilter")]),e._v("的注入,以及一个内部"),a("code",[e._v("StanzaListener")]),e._v("实现。有关更多信息,请参见"),a("a",{attrs:{href:"https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/XMPPConnection.html#addAsyncStanzaListener%28org.jivesoftware.smack.StanzaListener,%20org.jivesoftware.smack.filter.StanzaFilter%29",target:"_blank",rel:"noopener noreferrer"}},[e._v("Javadoc"),a("OutboundLink")],1),e._v("。")]),e._v(" "),a("p",[e._v("版本 4.3 为"),a("code",[e._v("ChatMessageListeningEndpoint")]),e._v("引入了"),a("code",[e._v("payload-expression")]),e._v("属性。传入的"),a("code",[e._v("org.jivesoftware.smack.packet.Message")]),e._v("表示求值上下文的根对象。当你使用"),a("a",{attrs:{href:"#xmpp-extensions"}},[e._v("XMPP 扩展")]),e._v("时,此选项很有用。例如,对于 GCM 协议,我们可以通过使用以下表达式来提取主体:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("payload-expression=\"getExtension('google:mobile:data').json\"\n")])])]),a("p",[e._v("下面的示例提取了 XHTML 协议的主体:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('payload-expression="getExtension(T(org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension).NAMESPACE).bodies[0]"\n')])])]),a("p",[e._v("为了简化对 XMPP 消息中扩展的访问,将"),a("code",[e._v("extension")]),e._v("变量添加到"),a("code",[e._v("EvaluationContext")]),e._v("中。请注意,它是在消息中仅存在一个扩展名时添加的。上面显示"),a("code",[e._v("namespace")]),e._v("操作的示例可以简化为以下示例:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('payload-expression="#extension.json"\npayload-expression="#extension.bodies[0]"\n')])])]),a("h4",{attrs:{id:"出站消息通道适配器"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#出站消息通道适配器"}},[e._v("#")]),e._v(" 出站消息通道适配器")]),e._v(" "),a("p",[e._v("你还可以通过使用出站消息通道适配器向 XMPP 上的其他用户发送聊天消息。"),a("code",[e._v("outbound-channel-adapter")]),e._v("元素为 XMPP 出站消息通道适配器提供配置支持。")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n')])])]),a("p",[e._v("适配器期望其输入(至少)是类型"),a("code",[e._v("java.lang.String")]),e._v("的有效负载和"),a("code",[e._v("XmppHeaders.CHAT_TO")]),e._v("的标头值,该值指定消息应发送给哪个用户。要创建消息,你可以使用类似于以下内容的 Java 代码:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('Message xmppOutboundMsg = MessageBuilder.withPayload("Hello, XMPP!" )\n\t\t\t\t\t\t.setHeader(XmppHeaders.CHAT_TO, "userhandle")\n\t\t\t\t\t\t.build();\n')])])]),a("p",[e._v("你还可以通过使用 XMPP Header-Enricher 支持来设置 header,如下例所示:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n\t\n\n')])])]),a("p",[e._v("从版本 4.3 开始,包扩展支持已添加到"),a("code",[e._v("ChatMessageSendingMessageHandler")]),e._v("(在 XML 配置中是"),a("code",[e._v("")]),e._v(")。随着常规的"),a("code",[e._v("String")]),e._v("和"),a("code",[e._v("org.jivesoftware.smack.packet.Message")]),e._v("有效负载,现在可以发送有效负载为"),a("code",[e._v("org.jivesoftware.smack.packet.ExtensionElement")]),e._v("(填充到"),a("code",[e._v("org.jivesoftware.smack.packet.Message.addExtension()")]),e._v(")而不是"),a("code",[e._v("setBody()")]),e._v("的消息。为了方便起见,我们为"),a("code",[e._v("ChatMessageSendingMessageHandler")]),e._v("添加了一个"),a("code",[e._v("extension-provider")]),e._v("选项。它允许你注入"),a("code",[e._v("org.jivesoftware.smack.provider.ExtensionElementProvider")]),e._v(",从而在运行时针对有效负载构建"),a("code",[e._v("ExtensionElement")]),e._v("。在这种情况下,有效负载必须是 JSON 或 XML 格式的字符串,这取决于 XEP 协议。")]),e._v(" "),a("h3",{attrs:{id:"xmpp-存在"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#xmpp-存在"}},[e._v("#")]),e._v(" XMPP 存在")]),e._v(" "),a("p",[e._v("XMPP 还支持广播状态。你可以利用这种能力,让那些把你列入名册的人看到你的状态变化。这种情况经常发生在你的即时通讯客户身上。你改变你的离开状态并设置一个离开消息,每个在他们的花名册上有你的人看到你的图标或用户名的变化,以反映这个新的状态,并可能看到你的新的“离开”消息。如果你希望接收通知或通知他人状态更改,可以使用 Spring Integration 的“存在”适配器。")]),e._v(" "),a("h4",{attrs:{id:"入站存在消息通道适配器"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#入站存在消息通道适配器"}},[e._v("#")]),e._v(" 入站存在消息通道适配器")]),e._v(" "),a("p",[e._v("Spring 集成提供了一种入站存在消息通道适配器,其支持从系统中在你的花名册上的其他用户接收存在事件。为此,适配器代表用户“登录”,注册"),a("code",[e._v("RosterListener")]),e._v(",并将接收到的存在更新事件作为消息转发到由"),a("code",[e._v("channel")]),e._v("属性标识的通道。消息的有效负载是"),a("code",[e._v("org.jivesoftware.smack.packet.Presence")]),e._v("对象(参见"),a("a",{attrs:{href:"https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/packet/Presence.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("https://www.igniterealtime.org/builds/smack/docs/latest/javadoc/org/jivesoftware/smack/packet/Presence.html"),a("OutboundLink")],1),e._v(")。")]),e._v(" "),a("p",[a("code",[e._v("presence-inbound-channel-adapter")]),e._v("元素为 XMPP 入站消息通道适配器提供配置支持。以下示例配置了入站存在消息通道适配器:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n')])])]),a("p",[e._v("除了通常的属性外,此适配器还需要对 XMPP 连接的引用。这个适配器是事件驱动的,并且是"),a("code",[e._v("Lifecycle")]),e._v("实现。它在启动时注册"),a("code",[e._v("RosterListener")]),e._v(",在停止时取消注册"),a("code",[e._v("RosterListener")]),e._v("。")]),e._v(" "),a("h4",{attrs:{id:"出站存在消息通道适配器"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#出站存在消息通道适配器"}},[e._v("#")]),e._v(" 出站存在消息通道适配器")]),e._v(" "),a("p",[e._v("Spring 集成还支持发送存在事件,以被网络中碰巧在他们的花名册上有你的其他用户看到。当你向出站存在消息通道适配器发送消息时,它会提取有效负载(预计类型为"),a("code",[e._v("org.jivesoftware.smack.packet.Presence")]),e._v(")并将其发送到 XMPP 连接,从而将你的存在事件广告到网络的其余部分。")]),e._v(" "),a("p",[a("code",[e._v("presence-outbound-channel-adapter")]),e._v("元素为 XMPP 出站消息通道适配器提供配置支持。下面的示例展示了如何配置出站消息通道适配器:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n')])])]),a("p",[e._v("它也可以是一个轮询消费者(如果它从一个可轮询通道接收消息),在这种情况下,你将需要注册一个 Poller。下面的示例展示了如何做到这一点:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n\t\n\n')])])]),a("p",[e._v("与它的入站对应物一样,它需要对 XMPP 连接的引用。")]),e._v(" "),a("table",[a("thead",[a("tr",[a("th"),e._v(" "),a("th",[e._v("如果你依赖 XMPP 连接 Bean("),a("a",{attrs:{href:"#xmpp-connection"}},[e._v("前面描述的")]),e._v(")的默认命名约定,并且在你的应用程序上下文中只配置了一个 XMPP 连接 Bean,那么你可以省略"),a("code",[e._v("xmpp-connection")]),e._v("属性。,在这种情况下,"),a("br"),e._v(",找到名为"),a("code",[e._v("xmppConnection")]),e._v("的 Bean 并将其注入适配器。")])])]),e._v(" "),a("tbody")]),e._v(" "),a("h3",{attrs:{id:"高级配置"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#高级配置"}},[e._v("#")]),e._v(" 高级配置")]),e._v(" "),a("p",[e._v("Spring Integration 的 XMPP 支持基于 Smack4.0API("),a("a",{attrs:{href:"https://www.igniterealtime.org/projects/smack/",target:"_blank",rel:"noopener noreferrer"}},[e._v("https://www.igniterealtime.org/projects/smack/"),a("OutboundLink")],1),e._v("),它允许对 XMPP 连接对象进行更复杂的配置。")]),e._v(" "),a("p",[e._v("由于"),a("a",{attrs:{href:"#xmpp-connection"}},[e._v("更早的陈述")]),e._v(","),a("code",[e._v("xmpp-connection")]),e._v("名称空间支持旨在简化基本的连接配置,并且仅支持几个常见的配置属性。然而,"),a("code",[e._v("org.jivesoftware.smack.ConnectionConfiguration")]),e._v("对象定义了大约 20 个属性,而为所有这些属性添加名称空间支持并不提供真正的价值。因此,对于更复杂的连接配置,可以将我们的"),a("code",[e._v("XmppConnectionFactoryBean")]),e._v("实例配置为常规 Bean,并将"),a("code",[e._v("org.jivesoftware.smack.ConnectionConfiguration")]),e._v("作为构造函数参数注入到"),a("code",[e._v("FactoryBean")]),e._v("。你可以在"),a("code",[e._v("ConnectionConfiguration")]),e._v("实例上直接指定所需的每个属性。( Bean 带有“P”名称空间的定义将很好地工作。)这样,你可以直接设置 SSL(或任何其他属性)。下面的示例展示了如何做到这一点:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n \n \n \n \n \n \n\n\n\n\n\n')])])]),a("p",[e._v("Smack API 还提供了静态初始化器,这可能会很有帮助。对于更复杂的情况(例如注册 SASL 机制),你可能需要执行某些静态初始化器。其中一个静态初始化器是"),a("code",[e._v("SASL认证")]),e._v(",它允许你注册受支持的 SASL 机制。对于这种复杂程度,我们建议在 XMPP 连接配置中使用 Spring Java 配置。这样,你就可以通过 Java 代码配置整个组件,并在适当的时候执行所有其他必要的 Java 代码,包括静态初始化器。下面的示例展示了如何在 Java 中配置带有 SASL(简单身份验证和安全层)的 XMPP 连接:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('@Configuration\npublic class CustomConnectionConfiguration {\n @Bean\n public XMPPConnection xmppConnection() {\n\tSASLAuthentication.supportSASLMechanism("EXTERNAL", 0); // static initializer\n\n\tConnectionConfiguration config = new ConnectionConfiguration("localhost", 5223);\n\tconfig.setKeystorePath("path_to_truststore.jks");\n\tconfig.setSecurityEnabled(true);\n\tconfig.setSocketFactory(SSLSocketFactory.getDefault());\n\treturn new XMPPConnection(config);\n }\n}\n')])])]),a("p",[e._v("有关使用 Java 进行应用程序上下文配置的更多信息,请参见"),a("a",{attrs:{href:"https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-java",target:"_blank",rel:"noopener noreferrer"}},[e._v("Spring Reference Manual"),a("OutboundLink")],1),e._v("中的以下部分。")]),e._v(" "),a("h3",{attrs:{id:"xmpp-消息头"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#xmpp-消息头"}},[e._v("#")]),e._v(" XMPP 消息头")]),e._v(" "),a("p",[e._v("Spring 集成 XMPP 适配器自动映射标准 XMPP 属性。默认情况下,这些属性通过使用["),a("code",[e._v("DefaultXmppHeaderMapper")]),e._v("](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xmpp/support/defaultxmppheadermapper.html)从 Spring integration"),a("code",[e._v("MessageHeaders")]),e._v("复制到 Spring integration"),a("code",[e._v("MessageHeaders")]),e._v("。")]),e._v(" "),a("p",[e._v("除非"),a("code",[e._v("DefaultXmppHeaderMapper")]),e._v("中的"),a("code",[e._v("requestHeaderNames")]),e._v("或"),a("code",[e._v("replyHeaderNames")]),e._v("属性明确指定,否则不会将任何用户定义的标题复制到或复制到 XMPP 消息。")]),e._v(" "),a("table",[a("thead",[a("tr",[a("th"),e._v(" "),a("th",[e._v("在映射用户定义的标头时,这些值还可以包含简单的通配符模式(例如“thing*”或“*thing”)。")])])]),e._v(" "),a("tbody")]),e._v(" "),a("p",[e._v("从版本 4.1 开始,"),a("code",[e._v("AbstractHeaderMapper")]),e._v("("),a("code",[e._v("DefaultXmppHeaderMapper")]),e._v("的超类)允许你为"),a("code",[e._v("requestHeaderNames")]),e._v("属性(除了"),a("code",[e._v("STANDARD_REQUEST_HEADERS")]),e._v(")配置"),a("code",[e._v("NON_STANDARD_HEADERS")]),e._v("令牌,以映射所有用户定义的标头。")]),e._v(" "),a("p",[a("code",[e._v("org.springframework.xmpp.XmppHeaders")]),e._v("类标识了"),a("code",[e._v("DefaultXmppHeaderMapper")]),e._v("要使用的默认标头:")]),e._v(" "),a("ul",[a("li",[a("p",[a("code",[e._v("xmpp_from")])])]),e._v(" "),a("li",[a("p",[a("code",[e._v("xmpp_subject")])])]),e._v(" "),a("li",[a("p",[a("code",[e._v("xmpp_thread")])])]),e._v(" "),a("li",[a("p",[a("code",[e._v("xmpp_to")])])]),e._v(" "),a("li",[a("p",[a("code",[e._v("xmpp_type")])])])]),e._v(" "),a("p",[e._v("从版本 4.3 开始,你可以通过在模式前面加上"),a("code",[e._v("!")]),e._v("来否定头映射中的模式。被否定的模式获得优先权,所以列表(如"),a("code",[e._v("STANDARD_REQUEST_HEADERS,thing1,thing*,!thing2,!thing3,qux,!thing1")]),e._v(")不映射"),a("code",[e._v("thing1")]),e._v("、"),a("code",[e._v("thing2")]),e._v("或"),a("code",[e._v("thing3")]),e._v("。该列表确实映射了标准标题加上"),a("code",[e._v("thing4")]),e._v("和"),a("code",[e._v("qux")]),e._v("。")]),e._v(" "),a("table",[a("thead",[a("tr",[a("th"),e._v(" "),a("th",[e._v("如果你有一个希望映射的以"),a("code",[e._v("!")]),e._v("开头的用户定义的头文件,则可以使用"),a("code",[e._v("\\")]),e._v("将其转义为:"),a("code",[e._v("STANDARD_REQUEST_HEADERS,\\!myBangHeader")]),e._v("。"),a("br"),e._v("在该示例中,标准的请求头文件和"),a("code",[e._v("!myBangHeader")]),e._v("文件将被映射。")])])]),e._v(" "),a("tbody")]),e._v(" "),a("h3",{attrs:{id:"xmpp-扩展"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#xmpp-扩展"}},[e._v("#")]),e._v(" XMPP 扩展")]),e._v(" "),a("p",[e._v("Extensions 将“可扩展”放在“可扩展消息传递和存在协议”中。")]),e._v(" "),a("p",[e._v("XMPP 是基于 XML 的,XML 是一种支持命名空间概念的数据格式。通过名称空间,你可以向 XMPP 添加原始规范中未定义的位。XMPP 规范特意只描述了一组核心特性:")]),e._v(" "),a("ul",[a("li",[a("p",[e._v("客户机如何连接到服务器")])]),e._v(" "),a("li",[a("p",[e._v("加密(SSL/TLS)")])]),e._v(" "),a("li",[a("p",[e._v("Authentication")])]),e._v(" "),a("li",[a("p",[e._v("服务器如何相互通信以中继消息")])]),e._v(" "),a("li",[a("p",[e._v("其他几个基本的构建模块")])])]),e._v(" "),a("p",[e._v("一旦实现了这一点,就有了一个 XMPP 客户机,可以发送任何类型的数据。然而,你可能需要做的不只是基础工作。例如,你可能需要在消息中包含格式(粗体、斜体等),而核心 XMPP 规范中并未对此进行定义。好吧,你可以想出一种方法来做到这一点,但是,除非其他所有人都像你一样做这件事,否则没有其他软件可以解释它(它们忽略它们无法理解的名称空间)。")]),e._v(" "),a("p",[e._v("为了解决这个问题,XMPP 标准基金会发布了一系列额外的文档,即"),a("a",{attrs:{href:"https://xmpp.org/extensions/xep-0001.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("XMPP 扩展协议"),a("OutboundLink")],1),e._v("。通常,每个 XEP 描述一个特定的活动(从消息格式到文件传输、多用户聊天等等)。它们还提供了一种标准格式,供每个人在该活动中使用。")]),e._v(" "),a("p",[e._v("Smack API 通过其"),a("code",[e._v("extensions")]),e._v("和"),a("code",[e._v("experimental")]),a("a",{attrs:{href:"https://www.igniterealtime.org/builds/smack/docs/latest/documentation/extensions/index.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("projects"),a("OutboundLink")],1),e._v("提供了许多 XEP 实现。从 Spring 集成版本 4.3 开始,你可以使用现有 XMPP 通道适配器的任何 XEP。")]),e._v(" "),a("p",[e._v("为了能够处理 XEPS 或任何其他定制的 XMPP 扩展,你必须提供 Smack 的"),a("code",[e._v("ProviderManager")]),e._v("预配置。你可以使用"),a("code",[e._v("static")]),e._v("Java 代码来实现这一点,如下例所示:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('ProviderManager.addIQProvider("element", "namespace", new MyIQProvider());\nProviderManager.addExtensionProvider("element", "namespace", new MyExtProvider());\n')])])]),a("p",[e._v("你还可以在特定实例中使用"),a("code",[e._v(".providers")]),e._v("配置文件,并使用 JVM 参数访问它,如下例所示:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("-Dsmack.provider.file=file:///c:/my/provider/mycustom.providers\n")])])]),a("p",[a("code",[e._v("mycustom.providers")]),e._v("文件可能如下:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v('\n\n\n query\n jabber:iq:time\n org.jivesoftware.smack.packet.Time\n\n\n\n query\n https://jabber.org/protocol/disco#items\n org.jivesoftware.smackx.provider.DiscoverItemsProvider\n\n\n\n subscription\n https://jabber.org/protocol/pubsub\n org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider\n\n\n')])])]),a("p",[e._v("例如,最流行的 XMPP 消息传递扩展是"),a("a",{attrs:{href:"https://developers.google.com/cloud-messaging/",target:"_blank",rel:"noopener noreferrer"}},[e._v("谷歌云消息"),a("OutboundLink")],1),e._v("。Smack 库为此目的提供了"),a("code",[e._v("org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider")]),e._v("。默认情况下,它使用"),a("code",[e._v("experimental.providers")]),e._v("资源在 Classpath 中用"),a("code",[e._v("smack-experimental")]),e._v("jar 注册该类,如下 Maven 示例所示:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("\x3c!-- GCM JSON payload --\x3e\n\n gcm\n google:mobile:data\n org.jivesoftware.smackx.gcm.provider.GcmExtensionProvider\n\n")])])]),a("p",[e._v("此外,"),a("code",[e._v("GcmPacketExtension")]),e._v("允许目标消息传递协议解析传入的数据包并构建传出的数据包,如下例所示:")]),e._v(" "),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("GcmPacketExtension gcmExtension = (GcmPacketExtension) xmppMessage.getExtension(GcmPacketExtension.NAMESPACE);\nString message = gcmExtension.getJson());\n")])])]),a("div",{staticClass:"language- extra-class"},[a("pre",{pre:!0,attrs:{class:"language-text"}},[a("code",[e._v("GcmPacketExtension packetExtension = new GcmPacketExtension(gcmJson);\nMessage smackMessage = new Message();\nsmackMessage.addExtension(packetExtension);\n")])])]),a("p",[e._v("有关更多信息,请参见本章前面的"),a("a",{attrs:{href:"#xmpp-message-inbound-channel-adapter"}},[e._v("入站消息通道适配器")]),e._v("和"),a("a",{attrs:{href:"#xmpp-message-outbound-channel-adapter"}},[e._v("出站消息通道适配器")]),e._v("。")])])}),[],!1,null,null,null);t.default=r.exports}}]);