控制 Bean 的管理接口

上一节的示例中,您对 bean 的管理接口几乎没有控制权。每个导出 bean 的所有public属性和方法分别作为 JMX 属性和操作公开。为了更精细地控制导出 bean 的哪些属性和方法实际上作为 JMX 属性和操作公开,Spring JMX 提供了一种全面且可扩展的机制来控制 bean 的管理接口。

使用MBeanInfoAssembler接口

在幕后,MBeanExporter委托给org.springframework.jmx.export.assembler.MBeanInfoAssembler接口的实现,该接口负责定义每个公开 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。您必须使用JmxAttributeSource接口的实现实例配置MetadataMBeanInfoAssembler,才能使其正常工作(没有默认值)。

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

ManagedResource注释的 bean 必须是公共的,公开操作或属性的方法也必须是公共的。

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

package org.springframework.jmx;

import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedAttribute;

@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 implements IJmxTestBean {

	private String name;
	private int age;

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

	public void setAge(int age) {
		this.age = 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 name;
	}

	@ManagedOperation(description="Add two numbers")
	@ManagedOperationParameters({
		@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();
	}

}

在前面的示例中,您可以看到 JmxTestBean 类用 ManagedResource 注释标记,并且此 ManagedResource 注释配置了一组属性。这些属性可用于配置由 MBeanExporter 生成的 MBean 的各个方面,并在后面的源代码级元数据类型中详细说明。

agename 属性都用 ManagedAttribute 注释标记,但对于 age 属性,只有 getter 被标记。这会导致这两个属性都作为属性包含在管理接口中,但 age 属性是只读的。

最后,add(int, int) 方法用 ManagedOperation 属性标记,而 dontExposeMe() 方法没有标记。这会导致管理接口在使用 MetadataMBeanInfoAssembler 时只包含一个操作 (add(int, int))。

以下配置展示了如何配置 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>

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

	<!-- 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="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 的源代码级元数据类型。

表 1. 源代码级元数据类型
目的 注释 注释类型

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

@ManagedResource

Class

将方法标记为 JMX 操作。

@ManagedOperation

Method

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

@ManagedAttribute

Method (仅 getter 和 setter)

定义操作参数的描述。

@ManagedOperationParameter@ManagedOperationParameters

Method

下表描述了可用于这些源代码级元数据类型的配置参数。

表 2. 源代码级元数据参数
参数 描述 应用于

ObjectName

MetadataNamingStrategy 用于确定受管资源的 ObjectName

ManagedResource

描述

设置资源、属性或操作的友好描述。

ManagedResourceManagedAttributeManagedOperationManagedOperationParameter

currencyTimeLimit

设置 currencyTimeLimit 描述符字段的值。

ManagedResourceManagedAttribute

defaultValue

设置 defaultValue 描述符字段的值。

ManagedAttribute

log

设置 log 描述符字段的值。

ManagedResource

logFile

设置 logFile 描述符字段的值。

ManagedResource

persistPolicy

设置 persistPolicy 描述符字段的值。

ManagedResource

persistPeriod

设置 persistPeriod 描述符字段的值。

ManagedResource

persistLocation

设置 persistLocation 描述符字段的值。

ManagedResource

persistName

设置 persistName 描述符字段的值。

ManagedResource

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="testBean" class="org.springframework.jmx.JmxTestBean">
		<property name="name" value="TEST"/>
		<property name="age" value="100"/>
	</bean>

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

</beans>

请注意,在前面的配置中,没有将任何 bean 传递给 MBeanExporter。但是,JmxTestBean 仍然注册,因为它标记有 ManagedResource 属性,并且 MetadataMBeanInfoAssembler 检测到这一点并投票将其包含在内。这种方法的唯一问题是 JmxTestBean 的名称现在具有业务意义。您可以通过更改 控制 bean 的 ObjectName 实例 中定义的 ObjectName 创建的默认行为来解决此问题。

使用 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 名称映射到方法名称列表。