在上一个例子中,并没有对bean的管理接口进行控制;每个bean的 所有 的 public属性和方法分别作为JMX的属性和操作来输出。 为了更细粒度的对那些输出bean的属性和方法进行控制,这些属性和方法实际是作为JMX的属性和操作输出的,Spring JMX提供了一个全面的可扩展的机制来控制你那些bean的管理接口。
在后台,MBeanExporter 委派接口 org.springframework.jmx.export.assembler.MBeanInfoAssembler 的一个实现,这个接口负责定义每个输出bean的管理接口。
类 org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler 是它的缺省实现,简单的定义了一个管理接口,输出所有public的属性和方法(如你在前面的例子见到的那样)。
Spring为接口 MBeanInfoAssembler 提供了两个另外的实现,允许你用源代码级元数据或任意接口来控制产生管理接口。
类 MetadataMBeanInfoAssembler 使你可以用源码级元数据来定义bean的管理接口。
元数据的读取由接口 org.springframework.jmx.export.metadata.JmxAttributeSource 封装。
Spring JMX提供了这个接口的两种实现,即开即用:
类 org.springframework.jmx.export.metadata.AttributesJmxAttributeSource针对公用属性(Commons Attributes),
而类 org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource 针对JDK5.0注解。
为了功能正常,类 MetadataMBeanInfoAssembler必须 和接口
JmxAttributeSource 的一个实现一起配置(这里 没有 缺省)。
在接下来的例子中,我们会用到公用属性元数据的方法。
要对一个输出到JMX的bean作标记,应该用属性 ManagedResource 来注解这个bean类。
在使用公用属性元数据方法的情况下,要能在包 org.springframework.jmx.metadata 中
找到它。
你希望作为操作输出的每个方法必须用属性 ManagedOperation 打上标记,
你希望输出的每个属性必须用属性ManagedAttribute打上标记。
在标记属性的时候,可以忽略getter的注解,或忽略分别创建一个只写或只读属性的setter。
下例展示了用公用属性元数据对前文见过的类JmxTestBean进行标记的情况:
package org.springframework.jmx;
/**
* @@org.springframework.jmx.export.metadata.ManagedResource
* (description="My Managed Bean", objectName="spring:bean=test",
* log=true, logFile="jmx.log", currencyTimeLimit=15, persistPolicy="OnUpdate",
* persistPeriod=200, persistLocation="foo", persistName="bar")
*/
public class JmxTestBean implements IJmxTestBean {
private String name;
private int age;
/**
* @@org.springframework.jmx.export.metadata.ManagedAttribute
* (description="The Age Attribute", currencyTimeLimit=15)
*/
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* @@org.springframework.jmx.export.metadata.ManagedAttribute
* (description="The Name Attribute", currencyTimeLimit=20,
* defaultValue="bar", persistPolicy="OnUpdate")
*/
public void setName(String name) {
this.name = name;
}
/**
* @@org.springframework.jmx.export.metadata.ManagedAttribute
* (defaultValue="foo", persistPeriod=300)
*/
public String getName() {
return name;
}
/**
* @@org.springframework.jmx.export.metadata.ManagedOperation
* (description="Add Two Numbers Together")
*/
public int add(int x, int y) {
return x + y;
}
public void dontExposeMe() {
throw new RuntimeException();
}
}
这里你可以看到,用属性 ManagedResource 来标记类 JmxTestBean,
这个 ManagedResource 是用一系列属性来配置的。
这些属性用于配置由 MBeanExporter 产生的MBean的不同方面。
在后续章节 第 20.3.4 节 “源代码级的元数据类型” 中,将有更详细的说明。
你将会注意到属性 age 和 name 是用属性 ManagedAttribute注解的,
但是在属性 age 这种情况下,只标记了getter。
这将使得这两个属性作为属性包括到管理接口中去,但属性 age 将是只读的。
最后,你会注意到方法 add(int, int) 是用 ManagedOperation 标记的,
而方法 dontExposeMe() 却不是。
这使得管理接口在使用 MetadataMBeanInfoAssembler 的时候只包含一个操作:add(int, int)。
下列代码显示了如何用 MetadataMBeanInfoAssembler 配置
MBeanExporter:
<beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="assembler" ref="assembler"/>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
<bean id="attributeSource"
class="org.springframework.jmx.export.metadata.AttributesJmxAttributeSource">
<property name="attributes">
<bean class="org.springframework.metadata.commons.CommonsAttributes"/>
</property>
</bean>
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="attributeSource"/>
</bean>
</beans>
这里你可以看到,用类 AttributesJmxAttributeSource 的一个实例来配置一个MetadataMBeanInfoAssembler bean,并通过装配属性将它传递给 MBeanExporter。如果Spring输出MBean是利用元数据驱动管理接口,则所有这些都是必需的。
为了激活JDK5.0注解,用它来进行管理接口定义,Spring提供了一套相当于Commons Attribut属性类的注解和一个策略接口 JmxAttributeSource 的实现类 AnnotationsJmxAttributeSource,
这个类允许 MBeanInfoAssembler 来读这些注解。
下例是一个用JDK5.0注解定义管理接口的bean:
package org.springframework.jmx;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.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();
}
}
如你所见,跟元数据定义的基本语法相比,改变很少。这个方法在后台启动的时候稍微有点慢,因为JDK5.0注解被转成了Commons Attributes使用的类。 但是,这仅只是一次性的消耗,JDK5.0注解对编译期的检查更有好处。
与上述被注解的类有关的XML配置如下所示:
<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>
<bean id="jmxAttributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
<!-- 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 ObjectName from annotation -->
<bean id="namingStrategy"
class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
<bean id="testBean" class="org.springframework.jmx.AnnotationTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
在Spring JMX中,可以使用下列源码级的元数据类型:
表 20.2. 源代码级的元数据类型
| 目的 | Commons Attributes属性 | JDK 5.0 注解 | 属性 / 注解类型 | |
|---|---|---|---|---|
把 Class 所有的实例标记为由JMX管理的资源 |
ManagedResource
|
@ManagedResource
|
类 | |
| 把方法标记为JMX的操作 |
ManagedOperation
|
@ManagedOperation
|
方法 | |
| 把getter或setter标记为JMX的半个属性 |
ManagedAttribute
|
@ManagedAttribute
|
方法(仅 getters 和 setters) | |
| 定义描述操作参数 |
ManagedOperationParameter
|
@ManagedOperationParameter 和 @ManagedOperationParameters
|
@ManagedOperationParameter 和 @ManagedOperationParameters
|
方法 |
接下来的配置参数可以用于这些源码级的元数据类型:
表 20.3. 源码级的元数据参数
| 参数 | 描述 | 适用于 |
|---|---|---|
ObjectName
|
由类 MetadataNamingStrategy 使用,决定一个管理资源的 ObjectName。
|
ManagedResource
|
description
|
设置资源、属性或操作友好的描述 |
ManagedResource、
ManagedAttribute、
ManagedOperation、
ManagedOperationParameter
|
currencyTimeLimit
|
描述符字段,用于设置 currencyTimeLimit 的值
|
ManagedResource、ManagedAttribute
|
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
|
为了进一步简化配置,Srping引入了接口 AutodetectCapableMBeanInfoAssembler ,
它扩展接口 MBeanInfoAssembler,增加了对自动检测MBean资源的支持。
如果你用 AutodetectCapableMBeanInfoAssembler 的一个实例来配置
MBeanExporter,则允许对将要暴露给JMX的所有bean进行“表决”。
即开既用,MetadataMBeanInfoAssembler 是接口 AutodetectCapableMBeanInfo 唯一的实现,
它“表决”将所有被属性 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="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
<!-- (for Commons Attributes-based metadata) -->
<bean id="attributeSource"
class="org.springframework.jmx.export.metadata.AttributesJmxAttributeSource">
<property name="attributes">
<bean class="org.springframework.metadata.commons.CommonsAttributes"/>
</property>
</bean>
<!-- (for Java5+ annotations-based metadata) -->
<!--
<bean id="attributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
-->
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="attributeSource"/>
</bean>
</beans>
注意,在这个配置中,没有传给 MBeanExporter 任何bean;但是,JmxTestBean将仍被注册,
因为属性 ManagedResource 为它做了标记,并且 MetadataMBeanInfoAssembler 发现了这一点,“表决”包括了它。
这种方法唯一的问题是,JmxTestBean 的名字有商业含义。
要解决这个问题,你可以修改创建ObjectName 的缺省行为,按照 第 20.4 节 “控制bean的 ObjectName
” 章节中所讲的那样去定义。
除了 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>
你可以看到,在为任一bean构造管理接口时,InterfaceBasedMBeanInfoAssembler
被配成使用接口 IJmxTestBean。由 InterfaceBasedMBeanInfoAssembler
处理的bean是 不 需要实现那些用于生成JMX管理接口的接口的,明白这一点非常重要。
在上面的例子中,接口 IJmxTestBean 用于构造所有bean的所有管理接口。
许多情况下,并不想这样,你可能想对不同的bean用不同的接口。
这种情况下,你可以通过属性 interfaceMappings 传一个 Properties 的实例给 InterfaceBasedMBeanInfoAssembler,
在这里,每个实体的键都是bean的名字,每个实体的值就是用逗号隔开的用于那个bean的接口的名字列表。
如果既没有通过属性 managedInterfaces 又没有通过属性 interfaceMappings 指定管理接口,那么 InterfaceBasedMBeanInfoAssembler 将反射到bean上,使用所有被该bean实现的接口来创建管理接口。
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接一个bean的基础上控制方法的暴露,使用 MethodNameMBeanInfoAssembler 的属性 methodMappings 把bean的名字映射到方法名字的列表上。