控制 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 中可用的注解:

Table 1. Spring JMX 注解
注解 适用于 描述

@ManagedResource

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

@ManagedNotification

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

@ManagedAttribute

方法(仅 getter 和 setter)

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

@ManagedMetric

方法(仅 getter)

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

@ManagedOperation

方法

将方法标记为 JMX 操作。

@ManagedOperationParameter

方法

为操作参数定义描述。

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

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