控制 Bean 的管理接口

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

使用MBeanInfoAssembler API

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

要将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 的接口名称的逗号分隔列表。

如果未通过 `managedInterfaces` 或 `interfaceMappings` 属性指定管理接口,则 `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>

在前面的示例中,您可以看到 `add` 和 `myOperation` 方法作为 JMX 操作暴露,而 `getName()`、`setName(String)` 和 `getAge()` 作为 JMX 属性的相应部分暴露。在前面的代码中,方法映射适用于暴露给 JMX 的 Bean。要逐个 Bean 控制方法暴露,您可以使用 `MethodNameMBeanInfoAssembler` 的 `methodMappings` 属性将 Bean 名称映射到方法名称列表。