控制您的 Bean 的管理接口
在前一节的示例中,
您对 bean 的管理接口几乎没有控制权。每个导出的 bean 的所有public
属性和方法分别被暴露为 JMX 属性和操作。为了更精细地控制导出的 bean 中
哪些属性和方法实际被暴露为 JMX 属性和操作,Spring JMX 提供了一套全面且可扩展的机制,
用于控制 bean 的管理接口。
使用MBeanInfoAssemblerAPI
在底层,MBeanExporter 会委托给 org.springframework.jmx.export.assembler.MBeanInfoAssembler 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 的。 |
以下示例展示了我们在创建 MBeanServer中使用的exporting.html#jmx-exporting-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() 方法则没有。当你使用 add(int, int) 时,
这将导致管理接口仅包含一个操作(MetadataMBeanInfoAssembler)。
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 中可用的注解:
| 注解 | 适用范围 | 描述 |
|---|---|---|
|
类 |
将某个 |
|
类 |
表示由被管理资源发出的 JMX 通知。 |
|
方法(仅包含 getter 和 setter) |
将一个 getter 或 setter 方法标记为 JMX 属性的一半。 |
|
方法(仅包含 getter) |
将一个 getter 方法标记为 JMX 属性,并添加描述符属性以表明它是一个指标(metric)。 |
|
方法 |
将一个方法标记为 JMX 操作。 |
|
方法 |
为操作参数定义一个描述。 |
下表描述了可在这些注解中使用的一些常见属性。有关更多详细信息,请参阅每个注解的 Javadoc。
| 属性 | 适用范围 | 描述 |
|---|---|---|
|
|
由 |
|
|
设置资源、通知、属性、指标或操作的描述。 |
|
|
设置 |
|
|
设置 |
|
|
设置 |
|
|
设置 |
|
|
设置 |
|
|
设置 |
|
|
设置 |
|
|
设置 |
|
|
设置操作参数的显示名称。 |
|
|
设置操作参数的索引。 |
使用AutodetectCapableMBeanInfoAssembler接口
为了进一步简化配置,Spring 提供了 AutodetectCapableMBeanInfoAssembler 接口,该接口扩展了 MBeanInfoAssembler 接口,以增加对 MBean 资源自动检测的支持。如果你使用 MBeanExporter 的实例来配置 AutodetectCapableMBeanInfoAssembler,它就可以对是否将某个 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 使用不同的接口。此时,你可以通过 InterfaceBasedMBeanInfoAssembler 属性向 Properties 传入一个 interfaceMappings 实例,其中每个条目的键是 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 地控制方法的暴露行为,您可以使用 methodMappings 的 MethodNameMBeanInfoAssembler 属性,将 Bean 名称映射到对应的方法名称列表。