跳到主要内容

控制 Bean 的管理接口

ChatGPT-4o 中英对照 Controlling the Management Interface of Your Beans

上一节的示例中,您对 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。你必须为 MetadataMBeanInfoAssembler 配置一个 JmxAttributeSource 接口的实现实例,以使其正常工作,因为没有默认配置。

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

备注

一个用 @ManagedResource 注解的 bean 必须是 public 的,公开操作或属性的方法也必须是 public 的。

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

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();
}

}
java

在前面的示例中,你可以看到 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>
xml

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

Spring JMX 注解

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

表 1. Spring JMX 注解

注解适用对象描述
@ManagedResourceClass 的所有实例标记为 JMX 管理资源。
@ManagedNotification表示由管理资源发出的 JMX 通知。
@ManagedAttribute方法(仅限 getter 和 setter)将 getter 或 setter 标记为 JMX 属性的一部分。
@ManagedMetric方法(仅限 getter)将 getter 标记为 JMX 属性,并添加描述符属性以指示它是一个度量。
@ManagedOperation方法将方法标记为 JMX 操作。
@ManagedOperationParameter方法为操作参数定义描述。

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

表 2. Spring JMX 注解属性

属性适用于描述
objectName@ManagedResourceMetadataNamingStrategy 使用以确定托管资源的 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,则允许其对暴露给 JMX 的 bean 的包含进行“投票”。

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

注意,在前面的配置中,没有 bean 被传递给 MBeanExporter。然而,AnnotationTestBean 仍然被注册,因为它被注解为 @ManagedResource,而 MetadataMBeanInfoAssembler 检测到这一点并投票将其包含在内。使用这种方法的唯一缺点是 AnnotationTestBean 的名称现在具有业务意义。您可以通过配置 ObjectNamingStrategy 来解决此问题,具体说明请参见控制 Bean 的 ObjectName 实例。您还可以在使用源级元数据:Java 注解中查看使用 MetadataNamingStrategy 的示例。

使用 Java 接口定义管理接口

除了 MetadataMBeanInfoAssembler 之外,Spring 还包含 InterfaceBasedMBeanInfoAssembler,它允许你根据接口集合中定义的方法集来限制公开的方法和属性。

虽然暴露 MBeans 的标准机制是使用接口和一个简单的命名方案,但 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();

}
java

该接口定义了在 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>
xml

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

在前面的例子中,IJmxTestBean 接口用于构建所有 bean 的管理接口。在很多情况下,这并不是期望的行为,你可能希望为不同的 bean 使用不同的接口。在这种情况下,你可以通过 interfaceMappings 属性向 InterfaceBasedMBeanInfoAssembler 传递一个 Properties 实例,其中每个条目的键是 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>
xml

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