27.3 bean的控制管理接口务

在前面的例子中,每个已经被暴露为JMX属性和操作的bean的所有public属性和方法,你都可以通过bean的管理接口来控制。你可以精确的控制你所暴露的bean上的哪个属性和方法作为JMX的属性和操作,Spring JMX提供了全面的、可扩展的机制来控制bean的管理接口。

27.3.1 MBeanInfoAssembler接口

底层实现上,MBeanExporter委托了一个org.springframework.jmx.export.assembler.MBeanInfoAssembler接口的实现来负责在每个已经被暴露的bean上定义管理接口。默认实现是org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler,对所有public的属性和方法(如你在前面例子中看到的)都会简单的定义一个管理接口。Spring对MBeanInfoAssembler接口提供了两种额外的实现,它允许使用源码级别的元数据或任意接口来控制管理接口的生成。

27.3.2 源码级别的元数据(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的。

下面的例子展示了用注解实现的JmxTestBean类:

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(); 
        } 

} 

可以看到JmxTestBean类被配置了一系列属性的ManagedResource注解标记。这些属性可以通过MBeanExporter产生配置了各种切面的MBean,详情请参考后面的27.3.3节,”源码级别的元数据类型“。

你也注意到了age和name属性都用了ManagedAttribute注解,但是在age属性上,只有getter上使用了。这会使得所有的属性在management接口中都作为属性,只有age属性是只读的。

最终,你会注意到,add(int, int)方法被标记为ManagedOperation属性,而dontExposeMe()这个方法却不是。当使用MetadataMBeanInfoAssembler时,管理接口只包含一个add(int,int)操作。

下面的配置展示了在使用MetadataMBeanInfoAssembler时如何配置MBeanExporter:

<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 the ObjectName from the 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> 

这里你将看到MetadataMBeanInfoAssembler被配置为AnnotationJmxAttributeSource的一个实例,并且通过assembler属性传递给MBeanExporter。这对于利用Spring暴露的MBean元数据驱动的管理接口来说是必须的。

27.3.3 源码级别的元数据类型

使用Spring JMX有以下几种源码级别的元数据类型:

  • 表 27.2. 源码级别的元数据类型
目标 注解 注解类型
把所有类的实例作为JMX管理的资源 @ManagedResource
将一个方法作为JMX的操作 @ManagedOperation 方法
将getter或setter标识为部分的JMX属性 @ManagedAttribute 方法(只限getter或setter)
对一个操作参数定义描述符 @ManagedOperationParameter@ManagedOperationParameters 方法

使用这些源码级别的元数据类型有以下的配置参数:

参数 描述 应用
ObjectName 使用元数据命名策略决定管理资源的ObjectName ManagedResource
description 设置友好的资源、属性和操作的描述 ManagedResource, ManagedAttribute, ManagedOperation, ManagedOperationParameter
currencyTimeLimit 设置currencyTimeLimit描述字段值 ManagedResource, ManagedAttribute
defaultValue 设置defaultValue描述字段值 ManagedAttribute
log 设置log描述字段值 ManagedResource
logFile 设置logFile描述字段值 ManagedResource
persistPolicy 置persistPolicy描述字段值 ManagedResource
persistPolicy 设置persistPolicy描述字段值 ManagedResource
persistPeriod 设置persistPeriod描述字段值 ManagedResource
persistLocation 设置persistLocation描述字段值 ManagedResource
persistName 设置persistName描述字段值 ManagedResource
name 设置操作参数展示的名字 ManagedOperationParameter
index 设置操作参数的索引 ManagedOperationParameter

27.3.4 AutodetectCapableMBeanInfoAssembler接口

为了进一步简化配置,Spring引入了继承了MBeanInfoAssembler接口来支持MBean资源的自动发现的AutodetectCapableMBeanInfoAssembler接口。如果你用AutodetectCapableMBeanInfoAssembler的实例配置了MBeanExporter,那么你可以对暴露给JMX的bean进行“投票”。

AutodetectCapableMBeanInfo的唯一实现是MetadataMBeanInfoAssembler,它将对被标记为ManagedResource的属性进行投票。默认使用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> 

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

</beans> 

注意,在这个配置中没有bean被传递给MBeanExporter;然而,JmxTestBean在被标记为ManagedResource属性,而且MetadataMBeanInfoAssembler会发现JmxTestBean并且投票把它包含进来时就被注册了。这种方法唯一问题是JmxTestBean的名字现在有业务含义。你可以通过更改ObjectName创建的默认行为来解决此问题,如27.4,“控制bean的ObjectName”。

27.3.5使用Java接口定义管理接口

除了MetadataMBeanInfoAssembler,Spring也提供了MetadataMBeanInfoAssembler,Spring也包含了InterfaceBasedMBeanInfoAssembler,它允许约束方法和用一些基于集合接口上定义的方法所暴露出来的属性。
虽然标准的暴露机制是使用接口和一个简单命名方案,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构建管理接口时会使用IJmxTestBean配置InterfaceBasedMBeanInfoAssembler。通过InterfaceBasedMBeanInfoAssembler处理的bean是不需要实现用来产生JMX管理接口的接口,明白这点很重要。
上面的例子中,所有bean的管理接口都是使用IJmxTestBean接口来构建。在许多情况下,不同的bean会使用不同的接口,而不是上面的单一行为。此例中,你可以通过interfaceMappings属性,来传递InterfaceBasedMBeanInfoAssembler的实例,这样key就是bean名字,value就是在这个bean上的用逗号分隔的方法列表。
如果即没有通过managedInterfaces或interfaceMappings属性指明一个管理接口,那么InterfaceBasedMBeanInfoAssembler会通过反射在使用所有实现了该bean的接口来创建一个管理接口。

27.3.6 使用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> 

上面实例你可以看到,增加了暴露给JMX操作的方法myOperation和getName、setName(String) 和 getAge()等JMX的属性。在上面的代码中,暴露给JMX的方法都和bean有映射的关系。为了控制一个bean一个bean的暴露,使用MethodNameMBeanInfoAssembler的methodMappings属性来映射bean的名字到方法名字列表上。