跳到主要内容

导出您的 Beans 到 JMX

ChatGPT-4o 中英对照 Exporting Your Beans to JMX

Spring 的 JMX 框架中的核心类是 MBeanExporter。这个类负责将你的 Spring bean 注册到 JMX MBeanServer 中。例如,考虑以下类:

public class JmxTestBean implements IJmxTestBean {

private String name;
private int age;

@Override
public int getAge() {
return age;
}

@Override
public void setAge(int age) {
this.age = age;
}

@Override
public void setName(String name) {
this.name = name;
}

@Override
public String getName() {
return name;
}

@Override
public int add(int x, int y) {
return x + y;
}

@Override
public void dontExposeMe() {
throw new RuntimeException();
}
}
java

要将此 bean 的属性和方法公开为 MBean 的属性和操作,可以在配置文件中配置一个 MBeanExporter 类的实例并传入该 bean,如以下示例所示:

@Configuration
public class JmxConfiguration {

@Bean
MBeanExporter exporter(JmxTestBean testBean) {
MBeanExporter exporter = new MBeanExporter();
exporter.setBeans(Map.of("bean:name=testBean1", testBean));
return exporter;
}

@Bean
JmxTestBean testBean() {
JmxTestBean testBean = new JmxTestBean();
testBean.setName("TEST");
testBean.setAge(100);
return testBean;
}
}
java

前面的配置片段中相关的 bean 定义是 exporter bean。beans 属性告诉 MBeanExporter,到底哪些 bean 必须导出到 JMX MBeanServer。在默认配置中,beans Map 中每个条目的键被用作对应条目值引用的 bean 的 ObjectName。您可以更改此行为,如控制 Bean 的 ObjectName 实例中所述。

通过此配置,testBean bean 作为 MBean 暴露在 ObjectName bean:name=testBean1 下。默认情况下,bean 的所有 public 属性都作为属性暴露,所有 public 方法(继承自 Object 类的方法除外)都作为操作暴露。

备注

MBeanExporter 是一个 Lifecycle bean(参见启动和关闭回调)。默认情况下,MBeans 会在应用程序生命周期中尽可能晚地导出。您可以配置导出发生的 phase,或者通过设置 autoStartup 标志来禁用自动注册。

创建一个 MBeanServer

前一节中显示的配置假设应用程序运行在一个已经有一个(且只有一个)MBeanServer正在运行的环境中。在这种情况下,Spring 会尝试定位正在运行的 MBeanServer 并将你的 bean 注册到该服务器(如果有的话)。当你的应用程序运行在一个有自己 MBeanServer 的容器中(例如 Tomcat 或 IBM WebSphere)时,这种行为是很有用的。

然而,这种方法在独立环境或运行在不提供 MBeanServer 的容器内时没有用。为了解决这个问题,你可以通过在配置中添加一个 org.springframework.jmx.support.MBeanServerFactoryBean 类的实例来声明性地创建一个 MBeanServer 实例。你还可以通过将 MBeanExporter 实例的 server 属性值设置为 MBeanServerFactoryBean 返回的 MBeanServer 值,确保使用特定的 MBeanServer,如下例所示:

<beans>

<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>

<!--
this bean needs to be eagerly pre-instantiated in order for the exporting to occur;
this means that it must not be marked as lazily initialized
-->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="server" ref="mbeanServer"/>
</bean>

<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>

</beans>
xml

在前面的示例中,MBeanServer 的实例是由 MBeanServerFactoryBean 创建的,并通过 server 属性提供给 MBeanExporter。当您提供自己的 MBeanServer 实例时,MBeanExporter 不会尝试定位正在运行的 MBeanServer,而是使用提供的 MBeanServer 实例。为了使其正常工作,您的类路径中必须有一个 JMX 实现。

重用现有的 MBeanServer

如果没有指定服务器,MBeanExporter 会尝试自动检测正在运行的 MBeanServer。这在大多数环境中有效,因为通常只使用一个 MBeanServer 实例。然而,当存在多个实例时,导出器可能会选择错误的服务器。在这种情况下,你应该使用 MBeanServeragentId 来指示要使用的实例,如以下示例所示:

<beans>
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<!-- indicate to first look for a server -->
<property name="locateExistingServerIfPossible" value="true"/>
<!-- search for the MBeanServer instance with the given agentId -->
<property name="agentId" value="MBeanServer_instance_agentId>"/>
</bean>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="server" ref="mbeanServer"/>
...
</bean>
</beans>
xml

对于现有 MBeanServer 具有动态(或未知)agentId 并通过查找方法获取的平台或情况,您应该使用工厂方法,如下例所示:

<beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="server">
<!-- Custom MBeanServerLocator -->
<bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/>
</property>
</bean>

<!-- other beans here -->

</beans>
xml

延迟初始化的 MBeans

如果你使用一个配置为延迟初始化的 MBeanExporter 来配置一个 bean,MBeanExporter 不会打破这个约定并避免实例化该 bean。相反,它会在 MBeanServer 中注册一个代理,并推迟从容器中获取 bean,直到代理上的第一次调用发生。

这同样会影响 FactoryBean 的解析,其中 MBeanExporter 将定期内省生成的对象,从而有效地触发 FactoryBean.getObject()。为了避免这种情况,请将相应的 bean 定义标记为 lazy-init。

MBeans 的自动注册

通过 MBeanExporter 导出的任何已经是有效 MBean 的 bean 将直接注册到 MBeanServer,无需 Spring 进一步干预。您可以通过将 autodetect 属性设置为 true 来使 MBean 被 MBeanExporter 自动检测,如下例所示:

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="autodetect" value="true"/>
</bean>

<bean name="spring:mbean=true" class="org.springframework.jmx.export.TestDynamicMBean"/>
xml

在前面的示例中,名为 spring:mbean=true 的 bean 已经是一个有效的 JMX MBean,并由 Spring 自动注册。默认情况下,自动检测到的用于 JMX 注册的 bean 会使用其 bean 名称作为 ObjectName。你可以覆盖此行为,具体细节请参见为 Bean 控制 ObjectName 实例

控制注册行为

考虑这样一种情况:Spring 的 MBeanExporter 尝试使用 ObjectName bean:name=testBean1MBeanServer 注册一个 MBean。如果已经有一个 MBean 实例使用相同的 ObjectName 注册过,默认行为是失败(并抛出 InstanceAlreadyExistsException)。

您可以精确控制当一个 MBean 注册到一个 MBeanServer 时会发生什么。Spring 的 JMX 支持允许三种不同的注册行为来控制当注册过程发现一个 MBean 已经在相同的 ObjectName 下注册时的注册行为。下表总结了这些注册行为:

表 1. 注册行为

注册行为说明
FAIL_ON_EXISTING这是默认的注册行为。如果一个 MBean 实例已经在相同的 ObjectName 下注册,那么正在注册的 MBean 将不会被注册,并且会抛出 InstanceAlreadyExistsException。现有的 MBean 不受影响。
IGNORE_EXISTING如果一个 MBean 实例已经在相同的 ObjectName 下注册,那么正在注册的 MBean 将不会被注册。现有的 MBean 不受影响,并且不会抛出 Exception。这在多个应用程序想要在共享的 MBeanServer 中共享一个公共 MBean 的情况下很有用。
REPLACE_EXISTING如果一个 MBean 实例已经在相同的 ObjectName 下注册,那么之前注册的现有 MBean 将被注销,新的 MBean 将在其位置注册(新的 MBean 实际上替换了之前的实例)。

前表中的值在 RegistrationPolicy 类中定义为枚举。如果你想更改默认的注册行为,需要将 MBeanExporter 定义中的 registrationPolicy 属性值设置为这些值之一。

下面的示例展示了如何将默认注册行为更改为 REPLACE_EXISTING 行为:

<beans>

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="registrationPolicy" value="REPLACE_EXISTING"/>
</bean>

<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>

</beans>
xml