将您的Bean导出到JMX

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

  • Java

  • Kotlin

public class JmxTestBean implements IJmxTestBean {

	private String name;
	private int age;

	@Override
	public int getAge() {
		return age;
	}

	@Override
	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public int add(int x, int y) {
		return x + y;
	}

	@Override
	public void dontExposeMe() {
		throw new RuntimeException();
	}
}
class JmxTestBean : IJmxTestBean {

	private lateinit var name: String
	private var age = 0

	override fun getAge(): Int {
		return age
	}

	override fun setAge(age: Int) {
		this.age = age
	}

	override fun setName(name: String) {
		this.name = name
	}

	override fun getName(): String {
		return name
	}

	override fun add(x: Int, y: Int): Int {
		return x + y
	}

	override fun dontExposeMe() {
		throw RuntimeException()
	}
}

要将此Bean的属性和方法作为MBean的属性和操作公开,您可以在配置文件中配置MBeanExporter类的实例,并将Bean作为参数传入,如下例所示:

  • Java

  • Kotlin

  • XML

@Configuration
public class JmxConfiguration {

	@Bean
	MBeanExporter exporter(JmxTestBean testBean) {
		MBeanExporter exporter = new MBeanExporter();
		exporter.setBeans(Map.of("bean:name=testBean1", testBean));
		return exporter;
	}

	@Bean
	JmxTestBean testBean() {
		JmxTestBean testBean = new JmxTestBean();
		testBean.setName("TEST");
		testBean.setAge(100);
		return testBean;
	}
}
@Configuration
class JmxConfiguration {

	@Bean
	fun exporter(testBean: JmxTestBean) = MBeanExporter().apply {
		setBeans(mapOf("bean:name=testBean1" to testBean))
	}

	@Bean
	fun testBean() = JmxTestBean().apply {
		name = "TEST"
		age = 100
	}
}
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
	   https://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- this bean must not be lazily initialized if the exporting is to happen -->
	<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
		<property name="beans">
			<map>
				<entry key="bean:name=testBean1" value-ref="testBean"/>
			</map>
		</property>
	</bean>

	<bean id="testBean" class="org.example.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作为ObjectName bean:name=testBean1下的MBean公开。默认情况下,Bean的所有public属性都公开为属性,所有public方法(继承自Object类的方法除外)都公开为操作。

MBeanExporter是一个Lifecycle Bean(参见启动和关闭回调)。默认情况下,MBean在应用程序生命周期的尽可能晚的时间导出。您可以配置导出发生的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"/>

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

在前面的示例中,MBeanServer的实例由MBeanServerFactoryBean创建,并通过server属性提供给MBeanExporter。当您提供自己的MBeanServer实例时,MBeanExporter不会尝试找到正在运行的MBeanServer,而是使用提供的MBeanServer实例。为了使此方法正常工作,您必须在类路径中具有JMX实现。

重用现有的MBeanServer

如果没有指定服务器,MBeanExporter将尝试自动检测正在运行的MBeanServer。这在大多数环境中都能正常工作,因为只使用一个MBeanServer实例。但是,当存在多个实例时,导出器可能会选择错误的服务器。在这种情况下,您应该使用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>

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

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

延迟初始化的MBean

如果您使用也配置为延迟初始化的MBeanExporter配置Bean,则MBeanExporter不会破坏此约定,并避免实例化Bean。相反,它使用代理向MBeanServer注册,并将从容器中获取Bean推迟到对代理的第一次调用发生。

这也影响FactoryBean的解析,其中MBeanExporter将定期内省生成的objects,从而有效地触发FactoryBean.getObject()。为了避免这种情况,请将相应的Bean定义标记为lazy-init。

MBean的自动注册

通过MBeanExporter导出的任何已经是有效MBean的Bean都将按原样注册到MBeanServer,而无需Spring进一步干预。您可以通过将autodetect属性设置为true来使MBean被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=testBean1MBean注册到MBeanServer的情况。如果已在同一ObjectName下注册了MBean实例,则默认行为是失败(并抛出InstanceAlreadyExistsException)。

您可以精确控制将MBean注册到MBeanServer时发生的情况。Spring的JMX支持允许使用三种不同的注册行为来控制当注册过程发现已在同一ObjectName下注册了MBean时的注册行为。下表总结了这些注册行为

表1. 注册行为
注册行为 说明

FAIL_ON_EXISTING

这是默认的注册行为。如果已在同一ObjectName下注册了MBean实例,则不会注册正在注册的MBean,并且会抛出InstanceAlreadyExistsException。现有MBean不受影响。

IGNORE_EXISTING

如果已在同一ObjectName下注册了MBean实例,则不会注册正在注册的MBean。现有MBean不受影响,也不会抛出任何Exception。这在多个应用程序想要在共享MBeanServer中共享公共MBean的环境中非常有用。

REPLACE_EXISTING

如果已在同一ObjectName下注册了MBean实例,则会注销先前注册的现有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>