20.2. 将Bean暴露为JMX

Spring Framework

20.2. 将Bean暴露为JMX

Spring的JMX框架的核心类是 MBeanExporter。这个类负责获取Spring Bean, 然后将其注册到一个JMX MBeanServer 上。例如,仔细看看以下这几个类:

package org.springframework.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中的属性和方法暴露成为一个JMX MBean中的属性和操作,你只要在配置文件中简单的配置 MBeanExporter 一个实例,并且按照如下方法将这个Bean传入:

<beans>
  
  <!-- 如果要暴露,这个Bean一定  可以延迟初始化。-->
  
  
  <bean id="exporter" class="org.springframework.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="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

</beans>

上述配置片段中,关系最大的是 exporter Bean。beans 属性使得 MBeanExporter 知道要将哪个Bean输出到JMX的 MBeanServer 上去。 缺省配置中,beans 里的 Map 中的条目的key被用作相应条目值所引用的Bean的 ObjectName。 可以按照 第 20.4 节 “控制Bean的ObjectName 描述的那样改变这种行为。

在这个配置里,testBean 就以 bean:name=testBean1 这个 ObjectName 暴露成了一个MBean。 默认情况下,Bean中所有 public 的属性被暴露为属性,所有的 public 方法(除了那些从 Object 类继承过来的之外)都被暴露为操作。

20.2.1. 创建MBeanServer

上述配置是假设了应用程序运行在这样的环境中,一个有且仅有一个在运作中的 MBeanServer 的环境。 这种情况下,Spring将试图定位该 MBeanServer,之后将你的Bean(如果有的话)注册到该服务器上。 在自带 MBeanServer 的容器(例如Tomcat或者IBM WebSphere)中,这种行为是很有用的。

然而,在一个孤立的环境,或者不提供 MBeanServer 的容器中,这种方法毫无用武之地。 要处理这类问题,你应该创建一个 MBeanServer 实例,也就是声明式地将 org.springframework.jmx.support.MBeanServerFactoryBean 实例添加到你的配置里。 通过设置 MBeanExporterserver 属性的值, 你也可以确保 MBeanExporter 使用了 MBeanServerFactoryBean 返回的特定的 MBeanServer。 例如:

<beans>

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

  
  <!--
    要触发输出,必须预先实例化这个Bean,一定  可以标志为延迟初始化。
  -->
  
  
  
  <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>

这里有一个由 MBeanServerFactoryBean 创建的 MBeanServer 实例,它通过属性server提供给了 MBeanExporter。 当你提供了你自己的 MBeanServer 实例后,MBeanExporter 将使用该实例,且不再查找一个运行中的 MBeanServer。为了使之正确工作,当然了,你必须确保你的类路径上存在一个JMX实现。

20.2.2. 重用原有的MBeanServer

如果不指定任何服务器,MBeanExporter 将自动检测一个运行中的 MBeanServer。 这在只有一个 MBeanServer 的情况下可以奏效,当存在多个 MBeanServer的时候, MBeanExporter 可能会选错服务器。这种情况下,应该使用 MBeanServeragentId来指定究竟使用哪个服务器。

<beans>
   <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
     
     <!-- 说明首先要查找一个服务器 -->
     
     <property name="locateExistingServerIfPossible" value="true"/>
     
     <!-- 根据给定的agentId查找对应的 MBeanServer 实例 -->
     
     <property name="agentId" value="<MBeanServer instance agentId>"/>
   </bean>
   
   <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
     <property name="server" ref="mbeanServer"/>
   ...
   </bean>
</beans>

在某些平台中,MBeanServer 有一个动态(或者未知)的要通过lookup方法获取的 agentId。 这时就应该用 factory-method

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

20.2.3. 延迟初始化的MBean

如果你在 MBeanExporter 上配置了一个Bean,而这个 MBeanExporter 又配置了延迟初始化,那么 MBeanExporter 遵循这种契约,避免初始化这个Bean。相反,它会在 MBeanServer 上注册一个代理,推延从容器获取这个Bean的时刻,直到在代理端发生对它的第一次调用。

20.2.4. MBean的自动注册

要通过 MBeanExporter 输出的任意的Bean,并已是有效的MBean,将会被注册到 MBeanServer上去,而无须Spring的干预。 通过设置属性 autodetect 的值为true,MBeanExporter 将会自动探测MBean,如下:

<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"/>

这里,名为 spring:mbean=true 的Bean就已经是一个有效的JMX MBean了。它将会被Spring自动注册。 缺省情况下,那些做自动JMX注册的Bean的 ObjectName 就是它的Bean名称。 标题为 第 20.4 节 “控制Bean的ObjectName 的章节里详细的描述了如何覆盖(overridden)这种行为。

20.2.5. 控制注册行为

考虑这样的场景,一个Spring MBeanExporter 试图用 ObjectName 'bean:name=testBean1'MBeanServer 注册一个MBean。 如果已经存在一个同样 ObjectNameMBean 实例,缺省行为是失败(并且抛出一个 InstanceAlreadyExistsException)。

MBean 时发生哪种行为。 Spring的JMX支持提供三种不同的注册行为,以此来控制注册进程发现一个 MBean 已经用同样的 ObjectName 注册的情况。下面的表格总结了这些注册行为:

表 20.1. 注册行为

注册行为 说明

REGISTRATION_FAIL_ON_EXISTING

这是缺省的注册行为。如果一个 MBean 实例已经用同样的 ObjectName 注册了,Spring将不会再次注册一个 MBean 实例,并且会抛出一个 InstanceAlreadyExistsException 异常,这不会影响到原有的 MBean实例。

REGISTRATION_IGNORE_EXISTING

如果一个 MBean 实例已经用同样的 ObjectName 注册了,正在注册的 MBean 实例将不会被注册。这种情况下,原有的 MBean 实例不会受到影响,也不会抛出异常。

REGISTRATION_REPLACE_EXISTING

如果一个 MBean 实例已经用同样的 ObjectName 注册了,原有 MBean 的注册关系将会被解除,新的 MBean 会以同样的 ObjectName 注册(新的 MBean 实际上取代了原始的实例)。

MBeanRegistrationSupport 类以常量的方式定义了上述这些值(MBeanExporter 继承了这个父类)。如果你向改变缺省的注册行为,你只需要将 MBeanExporter 的属性 registrationBehaviorName 的值设置为上述这些值之一。

以下例子阐明了如何将缺省注册行为改变为 REGISTRATION_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="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>
    </bean>

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

</beans>