# JMX 支持

### JMX 支持

Spring 集成提供了用于接收和发布 JMX 通知的通道适配器。

你需要在项目中包含此依赖项：

Maven

```
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-jmx</artifactId>
    <version>5.5.9</version>
</dependency>
```

Gradle

```
compile "org.springframework.integration:spring-integration-jmx:5.5.9"
```

入站通道适配器允许轮询 JMX MBean 属性值，出站通道适配器允许调用 JMX MBean 操作。

#### 通知-监听通道适配器

通知监听通道适配器需要一个 JMX，用于发布此侦听器应注册到的通知的 MBean。一个非常简单的配置可能类似于以下内容：

```
<int-jmx:notification-listening-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=publisher"/>
```

|   |在启动时，`notification-listening-channel-adapter`使用`MBeanServer`注册，默认的 Bean 名称是`mbeanServer`，这恰好是使用 Spring 的`<context:mbean-server/>`元素时生成的相同的 Bean 名称。<br/>如果需要使用不同的名称，请确保包含`mbean-server`属性。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

适配器还可以接受对`NotificationFilter`和“handback”对象的引用，以提供与每个通知一起回传的某些上下文。这两个属性都是可选的。将前面的示例扩展到包括这些属性以及显式的`MBeanServer` Bean 名称，将产生以下示例：

```
<int-jmx:notification-listening-channel-adapter id="adapter"
    channel="channel"
    mbean-server="someServer"
    object-name="example.domain:name=somePublisher"
    notification-filter="notificationFilter"
    handback="myHandback"/>
```

通知侦听通道适配器是事件驱动的，并直接向`MBeanServer`注册。它不需要任何 Poller 配置。

|   |仅对于这个组件，`object-name`属性可以包含一个对象名模式（例如，<br/>“org.something:type=mytype，name=\*”）。，在这种情况下，适配器接收来自所有具有匹配该模式的对象名的 MBean 的通知。，此外，<br/>，`object-name`属性可以包含对`<util:list>`对象名称模式的 SPEL 引用，如下例所示：<br/><br/>```<br/><jmx:notification-listening-channel-adapter id="manyNotificationsAdapter"<br/>    channel="manyNotificationsChannel"<br/>    object-name="#{patterns}"/><br/><br/><util:list id="patterns"><br/>    <value>org.foo:type=Foo,name=*</value><br/>    <value>org.foo:type=Bar,name=*</value><br/></util:list><br/>```<br/>启用调试级别日志时，将记录定位的 MBean 的名称。|
|---| |

#### 通知-发布通道适配器

通知发布通道适配器相对简单。它在配置中只需要一个 JMX 对象名，如下例所示：

```
<context:mbean-export/>

<int-jmx:notification-publishing-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=publisher"/>
```

它还要求在上下文中存在`MBeanExporter`。这就是为什么`<context:mbean-export/>`元素也显示在前面的示例中的原因。

当消息被发送到此适配器的通道时，将根据消息内容创建通知。如果有效负载是`String`，则作为通知的`message`文本传递。任何其他有效负载类型都将作为通知的`userData`传递。

JMX 通知还具有`type`，并且应该是以点分隔的`String`。有两种方法可以提供`type`。始终优先考虑与`JmxHeaders.NOTIFICATION_TYPE`键关联的消息标头值。或者，你可以在配置中提供一个 fallback`default-notification-type`属性，如下例所示：

```
<context:mbean-export/>

<int-jmx:notification-publishing-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=publisher"
    default-notification-type="some.default.type"/>
```

#### 属性-轮询通道适配器

当你需要定期检查通过 MBean 作为托管属性提供的某些值时，属性轮询通道适配器非常有用。可以以与 Spring 集成中的任何其他轮询适配器相同的方式配置 Poller（或者可以依赖默认的 Poller）。需要`object-name`和`attribute-name`。还需要一个 mBeanServer 引用。但是，默认情况下，它会自动检查名为`mbeanServer`的 Bean，与通知侦听通道适配器[前面描述的](#jmx-notification-listening-channel-adapter)相同。下面的示例展示了如何使用 XML 配置属性轮询通道适配器：

```
<int-jmx:attribute-polling-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=someService"
    attribute-name="InvocationCount">
        <int:poller max-messages-per-poll="1" fixed-rate="5000"/>
</int-jmx:attribute-polling-channel-adapter>
```

#### 树轮询通道适配器

树轮询通道适配器查询 JMX MBean 树，并发送一条带有有效负载的消息，该负载是与查询匹配的对象的图形。默认情况下，MBean 映射到原语和简单对象，例如`Map`、`List`和数组。这样做允许简单地转换到（例如）JSON。还需要一个 mBeanServer 引用。但是，默认情况下，它会自动检查名为`mbeanServer`的 Bean，与通知监听通道适配器[前面描述的](#jmx-notification-listening-channel-adapter)相同。下面的示例展示了如何使用 XML 配置树轮询通道适配器：

```
<int-jmx:tree-polling-channel-adapter id="adapter"
    channel="channel"
    query-name="example.domain:type=*">
        <int:poller max-messages-per-poll="1" fixed-rate="5000"/>
</int-jmx:tree-polling-channel-adapter>
```

前面的示例包括所选 MBean 上的所有属性。你可以通过提供一个配置了适当过滤器的`MBeanObjectConverter`来过滤属性。可以通过使用`converter`属性提供转换器作为 Bean 定义的引用，或者可以使用内部`<bean/>`定义。 Spring 集成提供了一个`DefaultMBeanObjectConverter`，它可以在其构造函数参数中接受一个`MBeanAttributeFilter`。

Spring 集成提供了两个标准过滤器。`NamedFieldsMBeanAttributeFilter`允许你指定要包含的属性列表。`NotNamedFieldsMBeanAttributeFilter`允许你指定要排除的属性列表。你还可以实现自己的过滤器。

#### 操作调用通道适配器

操作调用通道适配器支持对 MBean 公开的任何托管操作的消息驱动调用。每个调用都需要调用操作名和目标 MBean 的对象名。这两个都必须由适配器配置显式提供，或者分别通过`JmxHeaders.OBJECT_NAME`和`JmxHeaders.OPERATION_NAME`消息头提供：

```
<int-jmx:operation-invoking-channel-adapter id="adapter"
    object-name="example.domain:name=TestBean"
    operation-name="ping"/>
```

然后适配器只需要能够发现`mbeanServer` Bean。如果需要一个不同的 Bean 名称，则为`mbean-server`属性提供一个引用。

消息的有效负载被映射到操作的参数（如果有的话）。带有`String`键的`Map`类型负载被视为名称/值对，而`List`或数组被作为简单的参数列表（没有显式的参数名称）传递。如果操作需要一个参数值，那么有效负载可以表示这个参数值。此外，如果操作不需要参数，则有效负载将被忽略。

如果你希望公开一个通道，以便由不需要包含头的消息调用单个公共操作，那么最后一个选项效果很好。

#### 操作调用出站网关

Spring 与操作调用通道适配器类似，集成还提供了一个操作调用出站网关，当需要返回值时，可以在处理非 void 操作时使用该网关。返回值作为消息有效负载发送到网关指定的`reply-channel`。下面的示例展示了如何使用 XML 配置操作调用出站网关：

```
<int-jmx:operation-invoking-outbound-gateway request-channel="requestChannel"
   reply-channel="replyChannel"
   object-name="o.s.i.jmx.config:type=TestBean,name=testBeanGateway"
   operation-name="testWithReturn"/>
```

如果不提供`reply-channel`属性，则将回复消息发送到由`IntegrationMessageHeaderAccessor.REPLY_CHANNEL`标头标识的通道。该消息头通常由消息流的入口点自动创建，例如任何网关组件。但是，如果消息流是通过手动创建 Spring 集成消息并将其直接发送到通道来启动的，则必须显式地指定消息头，或者使用`reply-channel`属性。

#### MBean 出口商

Spring 当`IntegrationMBeanExporter`被配置时，集成组件本身可以作为 MBean 公开。要创建`IntegrationMBeanExporter`的实例，定义 Bean 并提供对`MBeanServer`的引用和域名（如果需要）。可以省略这个域，在这种情况下，默认域是`org.springframework.integration`。下面的示例展示了如何声明`IntegrationMBeanExporter`实例和关联的`MBeanServer`实例：

```
<int-jmx:mbean-export id="integrationMBeanExporter"
            default-domain="my.company.domain" server="mbeanServer"/>

<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
    <property name="locateExistingServerIfPossible" value="true"/>
</bean>
```

|   |MBean 输出器与 Spring Core 中提供的输出器是正交的。<br/>它注册消息通道和消息处理程序，但不注册本身。<br/>通过使用标准`<context:mbean-export/>`标记，可以公开导出程序本身（以及 Spring 集成中的某些其他组件），<br/>导出程序附带了一些度量指标，例如，计算处理程序的数量和排队消息的数量。<br/><br/>它还具有一个有用的操作，如[有序停机管理运行](#jmx-mbean-shutdown)中所讨论的。|
|---| |

Spring Integration4.0 引入了`@EnableIntegrationMBeanExport`注释，以允许方便地配置类型`integrationMbeanExporter` Bean 的默认`IntegrationMBeanExporter`的`@Configuration`类级别的几个有用选项。下面的示例展示了如何配置这个 Bean:

```
@Configuration
@EnableIntegration
@EnableIntegrationMBeanExport(server = "mbeanServer", managedComponents = "input")
public class ContextConfiguration {

	@Bean
	public MBeanServerFactoryBean mbeanServer() {
		return new MBeanServerFactoryBean();
	}
}
```

如果需要提供更多选项或具有几个`IntegrationMBeanExporter`bean（例如用于不同的 MBean 服务器或避免与标准 Spring `MBeanExporter`冲突——例如通过`@EnableMBeanExport`），则可以将`IntegrationMBeanExporter`配置为通用 Bean。

##### MBean 对象名

应用程序中的所有`MessageChannel`、`MessageHandler`和`MessageSource`实例都由 MBean Exporter 包装，以提供管理和监视功能。下表列出了为每个组件类型生成的 JMX 对象名：

|Component Type|对象名称|
|--------------|----------------------------------------------------------------------------|
|MessageChannel|```<br/> `o.s.i:type=MessageChannel,name=<channelName>`<br/>```|
|MessageSource |```<br/> `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`<br/>```|
|MessageHandler|```<br/> `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`<br/>```|

源和处理程序的对象名中的`bean`属性接受下表中的一个值：

|  Bean Value  |说明|
|--------------|-----------------------------------------------------------------------------------------------------------------------------|
|   endpoint   |Bean 封闭端点的名称（例如`<service-activator>`），如果有一个|
|  anonymous   |Bean 表示封闭端点没有用户指定的名称，因此 JMX 名称是输入通道名称。|
|   internal   |对于众所周知的 Spring 集成缺省组件|
|handler/source|以上都不是。<br/>返回被监视对象的`toString()`方法（处理程序或源）|

可以通过在`object-name-static-properties`属性中提供对`Properties`对象的引用，将自定义元素追加到对象名。

此外，由于 Spring Integration3.0，你可以通过设置`ObjectNamingStrategy`属性来使用自定义[`ObjectNamingStrategy`]（https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/jmx/export/naming/objectnamingstrategy.html）。这样做可以对 MBean 的命名进行更大的控制，例如将所有集成 MBean 分组为“集成”类型。下面的示例展示了一种可能的自定义命名策略实现：

```
public class Namer implements ObjectNamingStrategy {

	private final ObjectNamingStrategy realNamer = new KeyNamingStrategy();
	@Override
	public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
		String actualBeanKey = beanKey.replace("type=", "type=Integration,componentType=");
		return realNamer.getObjectName(managedBean, actualBeanKey);
	}

}
```

`beanKey`参数是一个`String`，它包含标准的对象名，从`default-domain`开始，并包括任何其他的静态属性。前面的示例将标准`type`部分移动到`componentType`，并将`type`设置为“集成”，从而能够在一个查询中选择所有集成 MBean:`my.domain:type=Integration,\*`。这样做还可以在 VisualVM 等工具中将 bean 分组到域下的一个树条目下。

|   |默认的命名策略是[`MetadataNamingStrategy`]（https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/springframework/jmx/export/naming/metadatanamingstrategy.html）。<br/>导出人员将`default-domain`传播到该对象，以便在解析 Bean 键失败时让它生成一个后备对象名，<br/>如果你的自定义命名策略是`MetadataNamingStrategy`（或它的一个子类），导出程序不传播`default-domain`。<br/>你必须在策略 Bean 上对其进行配置。|
|---| |

从版本 5.1 开始；任何 Bean 名称（由对象名称中的`name`键表示），如果它们包含 Java 标识符（或句号`.`）中不允许的任何字符，都将被引用。

##### JMX 改进

版本 4.2 引入了一些重要的改进，代表了对框架中的 JMX 支持的一次相当大的改革。这些结果导致了 JMX 统计数据收集的性能的显著改善，并对其进行了更多的控制。然而，在一些特定的（不常见的）情况下，它对用户代码有一些影响。下文详细介绍了这些变化，必要时请谨慎从事。

@IntegrationManagedResource

与`@ManagedResource`注释类似，`@IntegrationManagedResource`将一个类标记为有资格作为 MBean 导出。但是，只有当应用程序上下文具有`IntegrationMBeanExporter`时，才会导出它。

Spring 某些集成类（在`org.springframework.integration`）包中）以前用`@ManagedResource`注释，现在用`@ManagedResource`和`@IntegrationManagedResource`注释。这是为了向后兼容（参见下一项）。这样的 MBean 通过任何上下文`MBeanServer`或`IntegrationMBeanExporter`导出（但不是两者都导出——如果两个出口商都存在，则如果 Bean 匹配`managed-components`模式，则 Bean 由集成出口商导出）。

MBean Exporter Bean 名称模式

以前，`managed-components`模式仅包含。如果 Bean 名称与其中一种模式相匹配，则该名称将被包括在内。现在，可以通过在模式前加上`!`来否定该模式。例如，`!thing*, things`匹配所有不以`thing`开头的 Bean 名称，但`things`除外。模式是从左到右计算的。第一场比赛（正面或负面）获胜，然后不再应用其他模式。

|   |将此语法添加到模式中会导致一个可能的（尽管可能不太可能）问题，<br/>如果你的 Bean 名为`"!thing"`，并且你在 MBean Exporter 的`managed-components`模式中包含了`!thing`的模式，它不再匹配；模式现在匹配所有未命名`thing`的 bean。<br/>在这种情况下，你可以使用`\`在模式中转义`!`。<br/>`\!thing`模式匹配名为`!thing`的 Bean 的 Bean 模式。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

整合-------------------------------------------------------

`IntegrationMBeanExporter`不再实现`SmartLifecycle`。这意味着`start()`和`stop()`操作不再可用于注册和取消注册 MBean。MBean 现在在上下文初始化期间注册，在上下文被销毁时取消注册。

##### 有序关停托管运行

MBean Exporter 允许一个 JMX 操作以有序的方式关闭应用程序。它是在停止 JVM 之前使用的。下面的示例展示了如何使用它：

```
public void stopActiveComponents(long howLong)
```

其用途和操作在[有序关机](./shutdown.html#jmx-shutdown)中进行了说明。