控制 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
。你必须为 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();
}
}
在前面的示例中,你可以看到 AnnotationTestBean
类使用 @ManagedResource
注解进行标注,并且这个 @ManagedResource
注解配置了一组属性。这些属性可用于配置由 MBeanExporter
生成的 MBean 的各个方面,并在后面的 Spring JMX 注解 中进行了更详细的解释。
age
和 name
属性都用 @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 暴露的 MBeans 所需的全部配置。
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
,则允许其对暴露给 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>
注意,在前面的配置中,没有 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();
}
该接口定义了在 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
属性向 InterfaceBasedMBeanInfoAssembler
传递一个 Properties
实例,其中每个条目的键是 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 名称映射到方法名称列表。