控制您的 Bean 的管理接口

上一节的示例中,您对 bean 的管理接口几乎没有控制权。每个导出的 bean 的所有 public 属性和方法分别被暴露为 JMX 属性和操作。为了对导出的 bean 中究竟哪些属性和方法被暴露为 JMX 属性和操作进行更细粒度的控制,Spring JMX 提供了一个全面且可扩展的机制来控制 bean 的管理接口。

使用 MBeanInfoAssembler API

在底层,MBeanExporter 将工作委托给 org.springframework.jmx.export.assembler.MBeanInfoAssembler API 的实现类,该类负责定义每个暴露的 bean 的管理接口。默认实现 org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler 定义了一个暴露所有公共属性和方法的管理接口(如您在上一节示例中看到的那样)。Spring 提供了另外两种 MBeanInfoAssembler 接口的实现,它们允许您通过使用源代码级元数据或任意接口来控制生成管理接口。

使用源代码级元数据:Java 注解

通过使用 MetadataMBeanInfoAssembler,您可以使用源代码级元数据为您的 bean 定义管理接口。元数据的读取由 org.springframework.jmx.export.metadata.JmxAttributeSource 接口封装。Spring JMX 提供了一个使用 Java 注解的默认实现,即 org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource。您必须为 MetadataMBeanInfoAssembler 配置一个 JmxAttributeSource 接口的实现实例才能使其正常工作,因为没有默认实现。

要标记一个 bean 以导出到 JMX,您应该使用 @ManagedResource 注解标注 bean 类。您必须使用 @ManagedOperation 注解标注您希望暴露为操作的每个方法,并使用 @ManagedAttribute 注解标注您希望暴露的每个属性。在标注属性时,您可以省略 getter 或 setter 的注解,以分别创建只写或只读属性。

使用 @ManagedResource 注解的 bean 必须是公共的,暴露操作或属性的方法也必须是公共的。

以下示例显示了我们在创建 MBeanServer中使用的 JmxTestBean 类的注解版本。

package org.springframework.jmx;

@ManagedResource(
		objectName="bean:name=testBean4",
		description="My Managed Bean",
		log=true,
		logFile="jmx.log",
		currencyTimeLimit=15,
		persistPolicy="OnUpdate",
		persistPeriod=200,
		persistLocation="foo",
		persistName="bar")
public class AnnotationTestBean {

	private int age;
	private String name;

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

	@ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15)
	public int getAge() {
		return this.age;
	}

	@ManagedAttribute(description="The Name Attribute",
			currencyTimeLimit=20,
			defaultValue="bar",
			persistPolicy="OnUpdate")
	public void setName(String name) {
		this.name = name;
	}

	@ManagedAttribute(defaultValue="foo", persistPeriod=300)
	public String getName() {
		return this.name;
	}

	@ManagedOperation(description="Add two numbers")
	@ManagedOperationParameter(name = "x", description = "The first number")
	@ManagedOperationParameter(name = "y", description = "The second number")
	public int add(int x, int y) {
		return x + y;
	}

	public void dontExposeMe() {
		throw new RuntimeException();
	}

}

在前面的示例中,您可以看到 AnnotationTestBean 类使用 @ManagedResource 注解进行标注,并且此 @ManagedResource 注解配置了一组属性。这些属性可用于配置由 MBeanExporter 生成的 MBean 的各个方面,并在Spring JMX 注解中进行更详细的解释。

agename 属性都使用 @ManagedAttribute 注解进行标注,但在 age 属性的情况下,只标注了 getter 方法。这使得这两个属性都作为管理属性包含在管理接口中,但 age 属性是只读的。

最后,add(int, int) 方法使用 @ManagedOperation 注解进行标注,而 dontExposeMe() 方法没有。当您使用 MetadataMBeanInfoAssembler 时,这使得管理接口只包含一个操作(add(int, int))。

AnnotationTestBean 类不需要实现任何 Java 接口,因为 JMX 管理接口完全派生自注解。

以下配置展示了如何配置 MBeanExporter 以使用 MetadataMBeanInfoAssembler

<beans>

	<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
		<property name="assembler" ref="assembler"/>
		<property name="namingStrategy" ref="namingStrategy"/>
		<property name="autodetect" value="true"/>
	</bean>

	<!-- will create management interface using annotation metadata -->
	<bean id="assembler"
			class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
		<property name="attributeSource" ref="jmxAttributeSource"/>
	</bean>

	<!-- will pick up the ObjectName from the annotation -->
	<bean id="namingStrategy"
			class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
		<property name="attributeSource" ref="jmxAttributeSource"/>
	</bean>

	<bean id="jmxAttributeSource"
			class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>

	<bean id="testBean" class="org.springframework.jmx.AnnotationTestBean">
		<property name="name" value="TEST"/>
		<property name="age" value="100"/>
	</bean>

</beans>

在前面的示例中,已配置一个 MetadataMBeanInfoAssembler bean,它使用 AnnotationJmxAttributeSource 类的一个实例,并通过 assembler 属性传递给 MBeanExporter。这就是利用 Spring 暴露的 MBean 的注解驱动管理接口所需的全部配置。

Spring JMX 注解

下表描述了可在 Spring JMX 中使用的注解

表 1. Spring JMX 注解
注解 应用于 描述

@ManagedResource

Class 的所有实例标记为 JMX 受管资源。

@ManagedNotification

指示由受管资源发出的 JMX 通知。

@ManagedAttribute

方法(仅 getter 和 setter)

将 getter 或 setter 标记为 JMX 属性的一部分。

@ManagedMetric

方法(仅 getter)

将 getter 标记为 JMX 属性,并添加描述符属性以指示它是度量。

@ManagedOperation

方法

将方法标记为 JMX 操作。

@ManagedOperationParameter

方法

定义操作参数的描述。

下表描述了这些注解中可用的一些常用属性。有关更多详细信息,请查阅每个注解的 Javadoc。

表 2. Spring JMX 注解属性
属性 应用于 描述

objectName

@ManagedResource

MetadataNamingStrategy 使用,用于确定受管资源的 ObjectName

description

@ManagedResource, @ManagedNotification, @ManagedAttribute, @ManagedMetric, @ManagedOperation, @ManagedOperationParameter

设置资源、通知、属性、度量或操作的描述。

currencyTimeLimit

@ManagedResource, @ManagedAttribute, @ManagedMetric

设置 currencyTimeLimit 描述符字段的值。

defaultValue

@ManagedAttribute

设置 defaultValue 描述符字段的值。

log

@ManagedResource

设置 log 描述符字段的值。

logFile

@ManagedResource

设置 logFile 描述符字段的值。

persistPolicy

@ManagedResource, @ManagedMetric

设置 persistPolicy 描述符字段的值。

persistPeriod

@ManagedResource, @ManagedMetric

设置 persistPeriod 描述符字段的值。

persistLocation

@ManagedResource

设置 persistLocation 描述符字段的值。

persistName

@ManagedResource

设置 persistName 描述符字段的值。

name

@ManagedOperationParameter

设置操作参数的显示名称。

index

@ManagedOperationParameter

设置操作参数的索引。

使用 AutodetectCapableMBeanInfoAssembler 接口

为了进一步简化配置,Spring 包含了 AutodetectCapableMBeanInfoAssembler 接口,该接口扩展了 MBeanInfoAssembler 接口,以增加对 MBean 资源自动检测的支持。如果您使用 AutodetectCapableMBeanInfoAssembler 的实例配置 MBeanExporter,则允许其“投票”决定是否包含 bean 以暴露给 JMX。

AutodetectCapableMBeanInfo 接口的唯一实现是 MetadataMBeanInfoAssembler,它投票包含任何使用 ManagedResource 属性标记的 bean。在这种情况下,默认方法是使用 bean 名称作为 ObjectName,配置如下所示

<beans>

	<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
		<!-- notice how no 'beans' are explicitly configured here -->
		<property name="autodetect" value="true"/>
		<property name="assembler" ref="assembler"/>
	</bean>

	<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
		<property name="attributeSource">
			<bean class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
		</property>
	</bean>

	<bean id="testBean" class="org.springframework.jmx.AnnotationTestBean">
		<property name="name" value="TEST"/>
		<property name="age" value="100"/>
	</bean>

</beans>

请注意,在前面的配置中,没有将任何 bean 传递给 MBeanExporter。但是,AnnotationTestBean 仍然会被注册,因为它使用 @ManagedResource 注解进行标注,并且 MetadataMBeanInfoAssembler 检测到这一点并投票包含它。这种方法的唯一缺点是 AnnotationTestBean 的名称现在具有业务含义。您可以通过配置 ObjectNamingStrategy 来解决此问题,正如控制您的 Bean 的 ObjectName 实例中所解释的那样。您还可以在使用源代码级元数据:Java 注解中看到一个使用 MetadataNamingStrategy 的示例。

使用 Java 接口定义管理接口

除了 MetadataMBeanInfoAssembler,Spring 还包含了 InterfaceBasedMBeanInfoAssembler,它允许您根据一组接口中定义的方法来约束暴露的方法和属性。

虽然暴露 MBean 的标准机制是使用接口和简单的命名方案,但 InterfaceBasedMBeanInfoAssembler 通过移除命名约定的需要来扩展了此功能,允许您使用多个接口,并消除了您的 bean 实现 MBean 接口的需要。

考虑以下接口,它用于为我们之前展示的 JmxTestBean 类定义管理接口

public interface IJmxTestBean {

	public int add(int x, int y);

	public long myOperation();

	public int getAge();

	public void setAge(int age);

	public void setName(String name);

	public String getName();

}

此接口定义了在 JMX MBean 上作为操作和属性暴露的方法和属性。以下代码展示了如何配置 Spring JMX 使用此接口作为管理接口的定义

<beans>

	<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
		<property name="beans">
			<map>
				<entry key="bean:name=testBean5" value-ref="testBean"/>
			</map>
		</property>
		<property name="assembler">
			<bean class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
				<property name="managedInterfaces">
					<value>org.springframework.jmx.IJmxTestBean</value>
				</property>
			</bean>
		</property>
	</bean>

	<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
		<property name="name" value="TEST"/>
		<property name="age" value="100"/>
	</bean>

</beans>

在前面的示例中,InterfaceBasedMBeanInfoAssembler 被配置为在为任何 bean 构建管理接口时使用 IJmxTestBean 接口。重要的是要理解,由 InterfaceBasedMBeanInfoAssembler 处理的 bean 不需要实现用于生成 JMX 管理接口的接口。

在前面的情况下,IJmxTestBean 接口被用于构建所有 bean 的所有管理接口。在许多情况下,这不是期望的行为,您可能希望为不同的 bean 使用不同的接口。在这种情况下,您可以通过 interfaceMappings 属性将一个 Properties 实例传递给 InterfaceBasedMBeanInfoAssembler,其中每个条目的键是 bean 名称,每个条目的值是要用于该 bean 的接口名称的逗号分隔列表。

如果通过 managedInterfacesinterfaceMappings 属性没有指定管理接口,InterfaceBasedMBeanInfoAssembler 将通过反射检查 bean,并使用该 bean 实现的所有接口来创建管理接口。

使用 MethodNameBasedMBeanInfoAssembler

MethodNameBasedMBeanInfoAssembler 允许您指定要作为属性和操作暴露给 JMX 的方法名称列表。以下代码显示了一个示例配置

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
	<property name="beans">
		<map>
			<entry key="bean:name=testBean5" value-ref="testBean"/>
		</map>
	</property>
	<property name="assembler">
		<bean class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler">
			<property name="managedMethods">
				<value>add,myOperation,getName,setName,getAge</value>
			</property>
		</bean>
	</property>
</bean>

在前面的示例中,您可以看到 addmyOperation 方法被暴露为 JMX 操作,而 getName()setName(String)getAge() 被暴露为 JMX 属性的相应部分。在前面的代码中,方法映射适用于暴露给 JMX 的 bean。为了按 bean 控制方法暴露,您可以使用 MethodNameMBeanInfoAssemblermethodMappings 属性将 bean 名称映射到方法名称列表。