将您的Bean导出到JMX

Spring的JMX框架中的核心类是MBeanExporter。这个类负责将您的Spring beans注册到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>
	<!-- 如果要进行导出,则此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>

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

通过此配置,testBean bean将作为一个MBean暴露,其ObjectNamebean: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"/>

	<!--
	为了进行导出,此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>

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

重用现有的MBeanServer

如果未指定服务器,则MBeanExporter会尝试自动检测运行中的MBeanServer。这在大多数环境中都适用,只使用一个MBeanServer实例的情况下。但是,当存在多个实例时,导出器可能会选择错误的服务器。在这种情况下,您应该使用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具有通过查找方法检索的动态(或未知)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>

延迟初始化的MBeans

如果配置了一个同时配置为延迟初始化的MBeanExporter的bean,则MBeanExporter不会违反此约定并避免实例化该bean。相反,它会向MBeanServer注册一个代理,并推迟从容器获取该bean,直到代理上的第一次调用发生。

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

MBeans的自动注册

通过MBeanExporter导出并且已经是有效MBeans的任何bean都会原样注册到MBeanServer,Spring不需要进一步干预。您可以通过将autodetect属性设置为true来导致MBeans被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"/>

在上面的例子中,名为spring:mbean=true的bean已经是有效的JMX MBean,并由Spring自动注册。默认情况下,为JMX注册自动检测的bean将其bean名称用作ObjectName。您可以覆盖此行为,详细信息请参阅控制您的bean的ObjectName实例

控制注册行为

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

您可以精确控制当一个 MBeanMBeanServer 注册时发生的情况。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>