控制你的Beans的管理接口
在前一节中的示例中,你对bean的管理接口几乎没有控制权。每个导出的bean的所有public属性和方法都被分别作为JMX属性和操作暴露出来。为了更精细地控制哪些属性和方法真正作为JMX属性和操作被暴露,Spring JMX提供了一种全面且可扩展的机制来管理bean的管理接口。
使用 MBeanInfoAssembler API
在幕后,MBeanExporter 会委托给 org.springframework.jmx.export.assembler.MBeanInfoAssembler API 的实现来处理,该实现负责定义每个被公开(exposed)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 必须是公有的(public),暴露操作或属性的方法也必须是公有的。
以下示例展示了我们在创建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注解中更详细地解释这些属性。
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 | 类 | 将某个类的所有实例标记为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来解决这个问题,具体方法如Controlling ObjectName Instances for Your Beans中所解释的。你还可以在Using Source-level Metadata: Java Annotations中看到一个使用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属性向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名称与方法名称列表进行映射。