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 类继承过来的之外)都被暴露为操作。
上述配置是假设了应用程序运行在这样的环境中,一个有且仅有一个在运作中的 MBeanServer 的环境。
这种情况下,Spring将试图定位该 MBeanServer,之后将你的Bean(如果有的话)注册到该服务器上。
在自带 MBeanServer 的容器(例如Tomcat或者IBM WebSphere)中,这种行为是很有用的。
然而,在一个孤立的环境,或者不提供 MBeanServer 的容器中,这种方法毫无用武之地。
要处理这类问题,你应该创建一个 MBeanServer 实例,也就是声明式地将 org.springframework.jmx.support.MBeanServerFactoryBean 实例添加到你的配置里。
通过设置 MBeanExporter 的 server 属性的值,
你也可以确保 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实现。
如果不指定任何服务器,MBeanExporter 将自动检测一个运行中的 MBeanServer。
这在只有一个 MBeanServer 的情况下可以奏效,当存在多个 MBeanServer的时候,
MBeanExporter 可能会选错服务器。这种情况下,应该使用 MBeanServer 的 agentId来指定究竟使用哪个服务器。
<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>
如果你在 MBeanExporter 上配置了一个Bean,而这个 MBeanExporter 又配置了延迟初始化,那么 MBeanExporter 遵循这种契约,避免初始化这个Bean。相反,它会在 MBeanServer 上注册一个代理,推延从容器获取这个Bean的时刻,直到在代理端发生对它的第一次调用。
要通过 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)这种行为。
考虑这样的场景,一个Spring MBeanExporter 试图用 ObjectName 'bean:name=testBean1' 向 MBeanServer 注册一个MBean。
如果已经存在一个同样 ObjectName 的 MBean 实例,缺省行为是失败(并且抛出一个 InstanceAlreadyExistsException)。
MBean 时发生哪种行为。
Spring的JMX支持提供三种不同的注册行为,以此来控制注册进程发现一个 MBean 已经用同样的 ObjectName 注册的情况。下面的表格总结了这些注册行为:
表 20.1. 注册行为
| 注册行为 | 说明 |
|---|---|
|
这是缺省的注册行为。如果一个 |
|
如果一个 |
|
如果一个 |
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>