控制 Bean 的管理接口

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

使用 MBeanInfoAssembler 接口

在幕后,MBeanExporter 委托给 infra.jmx.export.assembler.MBeanInfoAssembler 接口的实现,该接口负责定义每个公开 Bean 的管理接口。默认实现 infra.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler 定义了一个管理接口,该接口公开所有公共属性和方法(正如您在前面各节的示例中看到的那样)。Infra 提供了 MBeanInfoAssembler 接口的另外两个实现,允许您使用源代码级元数据或任何任意接口来控制生成的管理接口。

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

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

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

使用 ManagedResource 注解的 Bean 必须是公共的,公开操作或属性的方法也必须如此。

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

package infra.jmx;

import infra.jmx.export.annotation.ManagedResource;
import infra.jmx.export.annotation.ManagedOperation;
import infra.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="infra.jmx.export.MBeanExporter">
    <property name="assembler" ref="assembler"/>
    <property name="namingStrategy" ref="namingStrategy"/>
    <property name="autodetect" value="true"/>
  </bean>

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

  <!-- 将使用注解元数据创建管理接口 -->
  <bean id="assembler"
      class="infra.jmx.export.assembler.MetadataMBeanInfoAssembler">
    <property name="attributeSource" ref="jmxAttributeSource"/>
  </bean>

  <!-- 将从注解中获取 ObjectName -->
  <bean id="namingStrategy"
      class="infra.jmx.export.naming.MetadataNamingStrategy">
    <property name="attributeSource" ref="jmxAttributeSource"/>
  </bean>

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

在前面的示例中,MetadataMBeanInfoAssembler bean 已配置有 AnnotationJmxAttributeSource 类的实例,并通过 assembler 属性传递给 MBeanExporter。这就是利用 Infra 公开的 MBean 的元数据驱动管理接口所需的全部内容。

源代码级元数据类型

下表描述了可在 Infra JMX 中使用的源代码级元数据类型:

Table 1. 源代码级元数据类型
用途 注解 注解类型

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

@ManagedResource

Class

将方法标记为 JMX 操作。

@ManagedOperation

Method

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

@ManagedAttribute

Method (仅限 getters 和 setters)

定义操作参数的描述。

@ManagedOperationParameter@ManagedOperationParameters

Method

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

Table 2. 源代码级元数据参数
参数 描述 适用于

ObjectName

MetadataNamingStrategy 用于确定托管资源的 ObjectName

ManagedResource

description

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

ManagedResource, ManagedAttribute, ManagedOperation, 或 ManagedOperationParameter

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 接口

为了进一步简化配置,Infra 包含了 AutodetectCapableMBeanInfoAssembler 接口,该接口扩展了 MBeanInfoAssembler 接口以添加对 MBean 资源自动检测的支持。如果您使用 AutodetectCapableMBeanInfoAssembler 的实例配置 MBeanExporter,则允许它对包含要向 JMX 公开的 Bean 进行“投票”。

AutodetectCapableMBeanInfo 接口的唯一实现是 MetadataMBeanInfoAssembler,它投票包含标记有 ManagedResource 属性的任何 Bean。这种情况下的默认方法是使用 Bean 名称作为 ObjectName,这会导致类似于以下的配置:

<beans>

  <bean id="exporter" class="infra.jmx.export.MBeanExporter">
    <!-- 注意这里没有明确配置 'beans' -->
    <property name="autodetect" value="true"/>
    <property name="assembler" ref="assembler"/>
  </bean>

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

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

</beans>

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

使用 Java 接口定义管理接口

除了 MetadataMBeanInfoAssembler 之外,Infra 还包括 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 上的操作和属性公开的方法和属性。以下代码显示了如何配置 Infra JMX 以使用此接口作为管理接口的定义:

<beans>

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

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

</beans>

在前面的示例中,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="infra.jmx.export.MBeanExporter">
  <property name="beans">
    <map>
      <entry key="bean:name=testBean5" value-ref="testBean"/>
    </map>
  </property>
  <property name="assembler">
    <bean class="infra.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 名称映射到方法名称列表。