27.2 将Bean暴露给JMX

MBeanExporter是Spring JMX 框架中的核心类。它负责把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的属性和方法作为一个属性和MBean的操作暴露出来,你只需简单的在配置文件中配置MBeanExporter类的实例并把此bean传递进去,如下:

<beans> 
    <!-- this bean must not be lazily initialized if the exporting is to happen --> 
    <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> 

上面配置段中相关的bean定义就是导出的bean。beans的属性准确的告诉MBeanExporter,哪个bean必须暴露给JMX MBeanServer。默认配置,在beans Map中的每个key都是相应的value引用的bean的对象名称。可以像27.4中“控制bean的对象名称”描述的那样来修改此行为。
在这个配置下testBean被暴露为一个名字为bean:name=testBean1的MBean。默认,bean的所有公共的属性都会被暴露为属性并且所有的公共方法(那些从Object类继承的)都被会暴露为操作。

MBeanExporter是一个有生命周期bean(参考“启动和关闭回调”)并且MBeans会在应用程序默认的生命周期中尽可能迟的被暴露。可以通过在暴露的阶段配置或者通过设置自动启动标志位来禁止自动注册。

27.2.1 创建MBeanServer

假设上述的配置是在一个正在运行的应用环境中,并且他有一个(只有一个) MBeanServer已经在运行了。在这种情形下,Spring将会尝试定位正在运行的MBeanServer并把你的bean注册到它上面去。当你的应用是运行在一个诸如Tomcat、IBM WebSphere等有自己MBeanServer的容器内时,这个就非常有用。

然而这种方法在独立的环境或运行在一个没有提供MBeanServer的容器中是没用的。为了解决这个问题,你可以通过添加一个org.springframework.jmx.support.MBeanServerFactoryBean的类实例到你的配置中来创建一个MBeanServer实例。你还可以通过将MBeanServerFactoryBeanMBean返回的MBeanServer值赋给MBeanExporter的server属性使用指定的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> 

这是通过MBeanServerFactoryBean创建的一个MBeanServer实例,并通过server属性把它传递给MBeanExporter。当你自己提供MBeanServer实例时,MBeanExporter不在尝试寻找正在运行的MBeanServer,将直接使用提供的MBeanServer实例。为了让它正常运行,你必须(当然)在你的类路径下有一个JMX的实现。

27.2.2重用存在的MBeanServer

如果没有指明的server,那么MBeanExporter会尝试去自动探测一个正在运行的MBeanServer。这在大多数情况下只有一个MBeanServer实例的情况下正常运行,但是当有多个实例存在时,exporter可能会选择一个错误的server。在这种情况下,应该要指明你要用的MBeanServer agentId:

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

对于平台化或一些情形来说,应该使用factory-method来查找动态变化(未知)的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> 

27.2.3MBeans懒加载

如果你用MBeanExporter配置了一个bean,那么它也配置了延迟初始化,MBeanExporter在不破坏他们关联性的情况下会避免bean的实例化。相反,它会注册一个MBeanServer的代理,直到第一次通过代理向容器获取bean的时候才会初始化。

27.2.4MBeans自动注册

任何通过MBeanExporter暴露的bean都是一个有效的MBean,它和MBeanServer注一样,不需要Spring的介入。可以将MBeanExporter的autodetect属性设置为true,MBeans就可以自动被发现:

<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都是有效的JMX MBean,它会被Spring自动注册。默认,bean的名字会被用作ObjectName,JMX注册时自动被发现。这个动作可以被重写,详情参考27.4,“控制bean的ObjectNames”。

27.2.5控制注册的动作

考虑Spring MBeanExporter试图使用’bean:name=testBean1’作为ObjectName将MBean注册到MBeanServer的场景。如果一个MBean的实例已经注册了相同的名字,默认情况下这此注册行为将失败(抛出InstanceAlreadyExistsException异常)。
可以严格控制MBean在注册到MBeanServer上何时发生了什么的行为。Spring的JMX支持三种不同的注册行为来控制在注册过程中发现MBean有相同的ObjectName;这些注册方法总结如下:

  • 表27.1 注册方法
注册方法 解释
REGISTRATION_FAIL_ON_EXISTING 这是默认的注册方法。如果一个MBean实例已经被注册了相同的ObjectName,这个MBean不能注册,并且抛出InstanceAlreadyExistsException异常。已经存在的MBean不受影响。
REGISTRATION_IGNORE_EXISTING 如果一个MBean实例已经被注册了相同的ObjectName,这个MBean不能注册。已经存在的MBean不受影响,也没有异常抛出。这个设置在多个应用之间共享MBeanServer,共享MBean时非常有用。
REGISTRATION_REPLACE_EXISTING 如果一个MBean实例已经被注册了相同的ObjectName,之前存在的MBean将被没有注册的新MBean原地替换(新的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>