Exporting Your Beans to JMX

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

The core class in Spring’s JMX framework is the MBeanExporter. This class is responsible for taking your Spring beans and registering them with a JMX MBeanServer. For example, consider the following class: include-code::./JmxTestBean[tag=snippet,indent=0]

要将此 bean 的属性和方法公开为 MBean 的属性和操作,可以在配置文件中配置 MBeanExporter 类的一个实例,并将 bean 传递进去,如下面的示例所示:

To expose the properties and methods of this bean as attributes and operations of an MBean, you can configure an instance of the MBeanExporter class in your configuration file and pass in the bean, as the following example shows: include-code::./JmxConfiguration[tag=snippet,indent=0]

前面的配置片段中相关的 bean 定义是 exporter bean。beans 属性准确告诉 MBeanExporter 你的哪些 bean 必须导出到 JMX MBeanServer。在默认配置中,beans Map 中每个条目的键用作由相应条目值引用的 bean 的 ObjectName。你可以更改此行为,如 Controlling ObjectName Instances for Your Beans 中所述。

The pertinent bean definition from the preceding configuration snippet is the exporter bean. The beans property tells the MBeanExporter exactly which of your beans must be exported to the JMX MBeanServer. In the default configuration, the key of each entry in the beans Map is used as the ObjectName for the bean referenced by the corresponding entry value. You can change this behavior, as described in Controlling ObjectName Instances for Your Beans.

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

With this configuration, the testBean bean is exposed as an MBean under the ObjectName bean:name=testBean1. By default, all public properties of the bean are exposed as attributes and all public methods (except those inherited from the Object class) are exposed as operations.

MBeanExporterLifecycle Bean(请参阅 Startup and Shutdown Callbacks)。默认情况下,MBean 在应用程序生命周期中尽可能晚导出。您可以配置导出 phase 的时间,或通过设置 autoStartup 标志禁用自动注册。

MBeanExporter is a Lifecycle bean (see Startup and Shutdown Callbacks ). By default, MBeans are exported as late as possible during the application lifecycle. You can configure the phase at which the export happens or disable automatic registration by setting the autoStartup flag.

Creating an MBeanServer

preceding section 中显示的配置假定应用程序在已经运行一个(且只有一个)MBeanServer 的环境中运行。在这种情况下,Spring 尝试查找正在运行的 MBeanServer,并将你的 bean 注册到该服务器(如果有)。当你应用程序在其自己的 MBeanServer 中运行(例如 Tomcat 或 IBM WebSphere)时,此行为很有用。

The configuration shown in the preceding section assumes that the application is running in an environment that has one (and only one) MBeanServer already running. In this case, Spring tries to locate the running MBeanServer and register your beans with that server (if any). This behavior is useful when your application runs inside a container (such as Tomcat or IBM WebSphere) that has its own MBeanServer.

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

However, this approach is of no use in a standalone environment or when running inside a container that does not provide an MBeanServer. To address this, you can create an MBeanServer instance declaratively by adding an instance of the org.springframework.jmx.support.MBeanServerFactoryBean class to your configuration. You can also ensure that a specific MBeanServer is used by setting the value of the MBeanExporter instance’s server property to the MBeanServer value returned by an MBeanServerFactoryBean, as the following example shows:

<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 实施。

In the preceding example, an instance of MBeanServer is created by the MBeanServerFactoryBean and is supplied to the MBeanExporter through the server property. When you supply your own MBeanServer instance, the MBeanExporter does not try to locate a running MBeanServer and uses the supplied MBeanServer instance. For this to work correctly, you must have a JMX implementation on your classpath.

Reusing an Existing MBeanServer

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

If no server is specified, the MBeanExporter tries to automatically detect a running MBeanServer. This works in most environments, where only one MBeanServer instance is used. However, when multiple instances exist, the exporter might pick the wrong server. In such cases, you should use the MBeanServer agentId to indicate which instance to be used, as the following example shows:

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

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

For platforms or cases where the existing MBeanServer has a dynamic (or unknown) agentId that is retrieved through lookup methods, you should use factory-method, as the following example shows:

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

Lazily Initialized MBeans

如果您使用也已配置为延迟初始化的 MBeanExporter 配置一个 bean,则 MBeanExporter 不会打破此契约,并避免实例化该 bean。取而代之的是,它会使用 MBeanServer 注册一个代理,并推迟从容器中获取 bean,直至对该代理的首次调用发生。

If you configure a bean with an MBeanExporter that is also configured for lazy initialization, the MBeanExporter does not break this contract and avoids instantiating the bean. Instead, it registers a proxy with the MBeanServer and defers obtaining the bean from the container until the first invocation on the proxy occurs.

这也影响 FactoryBean 解析,因为 MBeanExporter 会定期反省生成的对象,从而有效触发 FactoryBean.getObject()。为了避免这种情况,请将相应的 bean 定义标记为延迟初始化。

This also affects FactoryBean resolution where MBeanExporter will regularly introspect the produced object, effectively triggering FactoryBean.getObject(). In order to avoid this, mark the corresponding bean definition as lazy-init.

Automatic Registration of MBeans

任何通过 MBeanExporter 导出且已经有效的 MBean 会在没有 Spring 进一步干预的情况下按原样注册到 MBeanServer。您可以通过将 autodetect 属性设置为 true 来让 MBeanExporter 自动检测 MBean,如下面的示例所示:

Any beans that are exported through the MBeanExporter and are already valid MBeans are registered as-is with the MBeanServer without further intervention from Spring. You can cause MBeans to be automatically detected by the MBeanExporter by setting the autodetect property to true, as the following example shows:

<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`。你可以覆盖此行为,如Controlling ObjectName Instances for Your Beans中所述。

In the preceding example, the bean called spring:mbean=true is already a valid JMX MBean and is automatically registered by Spring. By default, a bean that is autodetected for JMX registration has its bean name used as the ObjectName. You can override this behavior, as detailed in Controlling ObjectName Instances for Your Beans.

Controlling the Registration Behavior

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

Consider the scenario where a Spring MBeanExporter attempts to register an MBean with an MBeanServer by using the ObjectName bean:name=testBean1. If an MBean instance has already been registered under that same ObjectName, the default behavior is to fail (and throw an InstanceAlreadyExistsException).

您可以精确控制将 MBean 注册到 MBeanServer 时的操作。Spring 的 JMX 支持允许三种不同的注册行为来控制查找进程发现某个 MBean 已在相同 ObjectName 下注册时的注册行为。下表总结了这些注册行为:

You can control exactly what happens when an MBean is registered with an MBeanServer. Spring’s JMX support allows for three different registration behaviors to control the registration behavior when the registration process finds that an MBean has already been registered under the same ObjectName. The following table summarizes these registration behaviors:

Table 1. Registration Behaviors
Registration behavior Explanation

FAIL_ON_EXISTING

This is the default registration behavior. If an MBean instance has already been registered under the same ObjectName, the MBean that is being registered is not registered, and an InstanceAlreadyExistsException is thrown. The existing MBean is unaffected.

IGNORE_EXISTING

If an MBean instance has already been registered under the same ObjectName, the MBean that is being registered is not registered. The existing MBean is unaffected, and no Exception is thrown. This is useful in settings where multiple applications want to share a common MBean in a shared MBeanServer.

REPLACE_EXISTING

If an MBean instance has already been registered under the same ObjectName, the existing MBean that was previously registered is unregistered, and the new MBean is registered in its place (the new MBean effectively replaces the previous instance).

前表中的值定义为 RegistrationPolicy 类中的枚举。如果您希望更改默认注册行为,需要将 MBeanExporter 定义中的 registrationPolicy 属性值设置为其中一个值。

The values in the preceding table are defined as enums on the RegistrationPolicy class. If you want to change the default registration behavior, you need to set the value of the registrationPolicy property on your MBeanExporter definition to one of those values.

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

The following example shows how to change from the default registration behavior to the REPLACE_EXISTING behavior:

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