将您的 Bean 导出到 JMX

Infra JMX 框架中的核心类是 MBeanExporter。该类负责获取您的 Infra Bean 并将它们注册到 JMX MBeanServer。例如,考虑以下类:

package infra.jmx;

public class JmxTestBean implements IJmxTestBean {

  private String name;
  private int age;
  private boolean isSuperman;

  public int getAge() {
    return age;
  }

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

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

  public String getName() {
    return name;
  }

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

  public void dontExposeMe() {
    throw new RuntimeException();
  }
}

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

<beans>
  <!-- 如果要进行导出,则此 bean 不得延迟初始化 -->
  <bean id="exporter" class="infra.jmx.export.MBeanExporter" lazy-init="false">
    <property name="beans">
      <map>
        <entry key="bean:name=testBean1" value-ref="testBean"/>
      </map>
    </property>
  </bean>
  <bean id="testBean" class="infra.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>
</beans>

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

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

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

创建 MBeanServer

上一节 中显示的配置假设应用程序运行在一个已经运行了一个(且只有一个)MBeanServer 的环境中。在这种情况下,Infra 尝试定位正在运行的 MBeanServer 并将您的 Bean 注册到该服务器(如果有)。当您的应用程序在具有自己的 MBeanServer 的容器(如 Tomcat 或 IBM WebSphere)内运行时,此行为非常有用。

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

<beans>

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

  <!--
  此 bean 需要急切地预实例化才能进行导出;
  这意味着它不得标记为延迟初始化
  -->
  <bean id="exporter" class="infra.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="infra.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

</beans>

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

重用现有的 MBeanServer

如果未指定服务器,MBeanExporter 会尝试自动检测正在运行的 MBeanServer。这在大多数环境中都有效,其中仅使用一个 MBeanServer 实例。但是,当存在多个实例时,导出器可能会选择错误的服务器。在这种情况下,您应该使用 MBeanServer agentId 来指示要使用哪个实例,如下例所示:

<beans>
  <bean id="mbeanServer" class="infra.jmx.support.MBeanServerFactoryBean">
    <!-- 指示首先查找服务器 -->
    <property name="locateExistingServerIfPossible" value="true"/>
    <!-- 搜索具有给定 agentId 的 MBeanServer 实例 -->
    <property name="agentId" value="MBeanServer_instance_agentId>"/>
  </bean>
  <bean id="exporter" class="infra.jmx.export.MBeanExporter">
    <property name="server" ref="mbeanServer"/>
    ...
  </bean>
</beans>

对于通过查找方法检索现有 MBeanServer 具有动态(或未知)agentId 的平台或情况,您应该使用 factory-method,如下例所示:

<beans>
  <bean id="exporter" class="infra.jmx.export.MBeanExporter">
    <property name="server">
      <!-- 自定义 MBeanServerLocator -->
      <bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/>
    </property>
  </bean>

  <!-- 其他 bean 在这里 -->

</beans>

延迟初始化的 MBean

如果您使用也配置为延迟初始化的 MBeanExporter 配置 Bean,则 MBeanExporter 不会破坏此契约,并避免实例化该 Bean。相反,它向 MBeanServer 注册代理,并推迟从容器获取 Bean,直到在代理上发生第一次调用。

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

MBean 的自动注册

通过 MBeanExporter 导出且已经是有效 MBean 的任何 Bean 都将按原样注册到 MBeanServer,而无需 Infra 的进一步干预。您可以通过将 autodetect 属性设置为 true 来使 MBeanExporter 自动检测 MBean,如下例所示:

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

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

在前面的示例中,名为 spring:mbean=true 的 Bean 已经是一个有效的 JMX MBean,并且由 Infra 自动注册。默认情况下,自动检测用于 JMX 注册的 Bean 使用其 Bean 名称作为 ObjectName。您可以覆盖此行为,如 控制 Bean 的 ObjectName 实例 中详述。

控制注册行为

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

您可以精确控制当 MBeanMBeanServer 注册时发生的情况。Infra JMX 支持允许三种不同的注册行为,以控制当注册过程发现 MBean 已在相同的 ObjectName 下注册时的注册行为。下表总结了这些注册行为:

Table 1. 注册行为
注册行为 说明

FAIL_ON_EXISTING

这是默认的注册行为。如果已在相同的 ObjectName 下注册了 MBean 实例,则不会注册正在注册的 MBean,并且会抛出 InstanceAlreadyExistsException。现有的 MBean 不受影响。

IGNORE_EXISTING

如果已在相同的 ObjectName 下注册了 MBean 实例,则不会注册正在注册的 MBean。现有的 MBean 不受影响,并且不会抛出 Exception。这在多个应用程序希望在共享的 MBeanServer 中共享公共 MBean 的设置中很有用。

REPLACE_EXISTING

如果已在相同的 ObjectName 下注册了 MBean 实例,则先前注册的现有 MBean 将被注销,并且新的 MBean 将在其位置注册(新的 MBean 有效地替换了先前的实例)。

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

以下示例显示了如何从默认注册行为更改为 REPLACE_EXISTING 行为:

<beans>

  <bean id="exporter" class="infra.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="infra.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

</beans>