31. JMX

31.1 简介

Spring 中的 JMX 支持为您提供了 features,可以轻松,透明地将 Spring application 集成到 JMX 基础结构中。


JMX?

本章不是对 JMX 的介绍......它并不试图解释为什么人们可能想要使用 JMX(或者实际上 JMX 实际上代表的是什么)的动机。如果您是 JMX 的新手,请查看本章末尾的第 31.8 节,“更多资源”


具体来说,Spring 的 JMX 支持提供了四个核心 features:

  • 将任何 Spring bean 自动注册为 JMX MBean

  • 一种灵活的机制,用于控制 beans 的 management 接口

  • MBean 在 remote,JSR-160 连接器上的声明性暴露

  • 本地和 remote MBean 资源的简单代理

这些 features 旨在在不将 application 组件耦合到 Spring 或 JMX 接口和 classes 的情况下工作。实际上,在大多数情况下,application classes 不需要知道 order 中的 Spring 或 JMX 以利用 Spring JMX features。

31.2 将 beans 导出到 JMX

Spring 的 JMX framework 中的核心 class 是MBeanExporter。此 class 负责获取 Spring beans 并使用 JMX MBeanServer注册它们。对于 example,请考虑以下 class:

package org.springframework.jmx;

public class JmxTestBean implements IJmxTestBean {

    private String name;
    private int age;
    private boolean isSuperman;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public int add(int x, int y) {
        return x + y;
    }

    public void dontExposeMe() {
        throw new RuntimeException();
    }
}

要将此 bean 的 properties 和方法公开为 MBean 的属性和操作,只需在 configuration 文件中配置MBeanExporter class 的实例并传入 bean,如下所示:

<beans>
    <!-- this bean must not be lazily initialized if the exporting is to happen -->
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean1" value-ref="testBean"/>
            </map>
        </property>
    </bean>
    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>
</beans>

上述 configuration 片段中的相关 bean 定义是exporter bean。 beans property 告诉MBeanExporter究竟哪些 beans 必须导出到 JMX MBeanServer。在默认的 configuration 中,beans Map中每个条目的 key 用作相应条目 value 引用的 bean 的ObjectName。可以按第 31.4 节,“控制 beans 的 ObjectNames”中的描述更改此行为。

通过此 configuration,testBean bean 在ObjectName bean:name=testBean1下显示为 MBean。默认情况下,bean 的所有公共 properties 都作为属性公开,所有公共方法(从Object class 继承的公共方法)都公开为操作。

MBeanExporterLifecycle bean(请参阅称为“启动和关闭回调”的部分),默认情况下,在 application 生命周期中尽可能晚地导出 MBean。可以通过设置autoStartup flag 来配置 export 发生的phase或禁用自动注册。

31.2.1 创建 MBeanServer

上面的 configuration 假定 application 在一个(且只有一个)MBeanServer已经 running 的环境中运行。在这种情况下,Spring 将尝试找到 running MBeanServer并在该服务器上注册 beans(如果有的话)。当 application 在具有自己的MBeanServer的 Tomcat 或 IBM WebSphere 等容器内运行时,此行为很有用。

但是,这种方法在独立环境中没有用处,或者在不提供MBeanServer的容器内运行时。要解决此问题,您可以通过在 configuration 中添加org.springframework.jmx.support.MBeanServerFactoryBean class 的实例来声明性地创建MBeanServer实例。您还可以通过将MBeanExporterserver property 的 value 设置为MBeanServerFactoryBean返回的MBeanServer value 来确保使用特定的MBeanServer;例如:

<beans>

    <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>

    <!--
    this bean needs to be eagerly pre-instantiated in order for the exporting to occur;
    this means that it must not be marked as lazily initialized
    -->
    <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="server" ref="mbeanServer"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

这里MBeanServer的实例由MBeanServerFactoryBean创建,并通过服务器 property 提供给MBeanExporter。当您提供自己的MBeanServer实例时,MBeanExporter将不会尝试找到 running MBeanServer并将使用提供的MBeanServer实例。为了使其正常工作,您必须(当然)在 classpath 上有一个 JMX implementation。

31.2.2 重用现有的 MBeanServer

如果未指定服务器,则MBeanExporter会尝试自动检测 running MBeanServer。这适用于大多数只使用一个MBeanServer实例的环境,但是当存在多个实例时,导出器可能会选择错误的服务器。在这种情况下,应该使用MBeanServer agentId来指示要使用的实例:

<beans>
    <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
        <!-- indicate to first look for a server -->
        <property name="locateExistingServerIfPossible" value="true"/>
        <!-- search for the MBeanServer instance with the given agentId -->
        <property name="agentId" value="MBeanServer_instance_agentId>"/>
    </bean>
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="server" ref="mbeanServer"/>
        ...
    </bean>
</beans>

对于 platforms/cases,其中现有的MBeanServer具有通过查找方法检索的动态(或未知)agentId,应该使用factory-method

<beans>
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="server">
            <!-- Custom MBeanServerLocator -->
            <bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/>
        </property>
    </bean>

    <!-- other beans here -->

</beans>

31.2.3 Lazy-initialized MBeans

如果配置一个带有MBeanExporter的 bean 也配置为延迟初始化,那么MBeanExporter将不会 break 这个 contract 并且将避免实例化 bean。相反,它将使用MBeanServer注册代理,并将推迟从容器中获取 bean,直到代理上的第一次调用发生。

31.2.4 自动注册 MBean

通过MBeanExporter导出并且已经是有效 MBean 的任何 beans 都会使用MBeanServer注册 as-is,而无需 Spring 的进一步干预。通过将autodetect property 设置为trueMBeanExporter可以自动检测 MBean:

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="autodetect" value="true"/>
</bean>

<bean name="spring:mbean=true" class="org.springframework.jmx.export.TestDynamicMBean"/>

这里,名为spring:mbean=true的 bean 已经是一个有效的 JMX MBean,将由 Spring 自动注册。默认情况下,为 JMX 注册自动检测的 beans 将 bean name 用作ObjectName。可以在第 31.4 节,“控制 beans 的 ObjectNames”中详细说明此行为。

31.2.5 控制注册行为

考虑 Spring MBeanExporter尝试使用ObjectName 'bean:name=testBean1'MBeanServer注册MBean的情况。如果MBean实例已经在同一个ObjectName下注册,则默认行为是失败(并抛出InstanceAlreadyExistsException)。

当注册MBeanServer时,可以准确控制行为。当注册 process 发现MBean已经在同一个ObjectName下注册时,Spring 的 JMX 支持允许三种不同的注册行为来控制注册行为;这些注册行为总结在以下 table 中:

表格 1_.注册行为

注册行为说明
REGISTRATION_FAIL_ON_EXISTING这是默认的注册行为。如果MBean实例已在相同的ObjectName下注册,则正在注册的MBean将不会被注册,并且将抛出InstanceAlreadyExistsException。现有的MBean不受影响。
REGISTRATION_IGNORE_EXISTING如果MBean实例已在同一ObjectName下注册,则正在注册的MBean将不会注册。现有的MBean不受影响,不会抛出Exception。这在多个 applications 想要在共享MBeanServer中共享 common MBean的设置中很有用。
REGISTRATION_REPLACE_EXISTING如果MBean实例已经在相同的ObjectName下注册,则先前注册的现有MBean将被取消注册,新的MBean将在其位置注册(新的MBean将有效地替换先前的实例)。

上述值在MBeanRegistrationSupport class 上定义为常量(MBeanExporter class 派生自此超类)。如果要更改默认注册行为,只需将MBeanExporter定义的registrationBehaviorName property 的 value 设置为其中一个值即可。

以下 example 说明了如何实现从默认注册行为到REGISTRATION_REPLACE_EXISTING行为的更改:

<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="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

31.3 控制 beans 的 management 接口

在前面的示例中,您几乎无法控制 bean 的 management 接口;每个导出的 bean 的所有公共 properties 和方法分别作为 JMX 属性和操作公开。要精确地控制导出的 beans 的哪些 properties 和方法实际公开为 JMX 属性和操作,Spring JMX 提供了一个全面且可扩展的机制来控制 beans 的 management 接口。

31.3.1 MBeanInfoAssembler 接口

在幕后,MBeanExporter委托org.springframework.jmx.export.assembler.MBeanInfoAssembler接口的 implementation,负责定义正在公开的每个 bean 的 management 接口。默认的 implementation,org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler,只是定义了一个 management 接口,它公开了所有公共 properties 和方法(正如你在前面的例子中看到的那样)。 Spring 提供了另外两个MBeanInfoAssembler接口的 implementations,允许您使用 source-level 元数据或任意接口控制生成的 management 接口。

31.3.2 使用 source-level 元数据:Java annotations

使用MetadataMBeanInfoAssembler,您可以使用 source level metadata 定义 beans 的 management 接口。元数据的读取由org.springframework.jmx.export.metadata.JmxAttributeSource接口封装。 Spring JMX 提供了一个使用 Java annotations 的默认 implementation,即org.springframework.jmx.export.annotation.AnnotationJmxAttributeSourceMetadataMBeanInfoAssembler必须配置JmxAttributeSource接口的 implementation 实例才能正确功能(没有默认值)。

要将 export 标记为 export 到 JMX,您应该使用ManagedResource annotation 注释 bean class。您希望作为操作公开的每个方法都必须使用ManagedOperation annotation 标记,并且您希望公开的每个 property 必须使用ManagedAttribute annotation 标记。标记 properties 时,可以省略 getter 或 setter 的 annotation 以分别创建 write-only 或 read-only 属性。

ManagedResource带注释的 bean 必须是公共的,以及公开操作或属性的方法。

下面的 example 显示了之前看到的JmxTestBean class 的带注释的 version:

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 class 标有ManagedResource annotation,并且此ManagedResource annotation 配置了一组 properties。这些 properties 可用于配置MBeanExporter生成的 MBean 的各个方面,稍后将在标题为第 31.3.3 节,“Source-level 元数据类型”的部分中进行更详细的说明。

您还会注意到agename properties 都使用ManagedAttribute annotation 注释,但在age property 的情况下,只标记了 getter。这将导致这两个 properties 作为属性包含在 management 接口中,但age属性将为 read-only。

最后,您会注意到add(int, int)方法标有ManagedOperation属性,而dontExposeMe()方法则没有。这将导致 management 接口在使用MetadataMBeanInfoAssembler时仅包含一个操作add(int, int)

下面的 configuration 显示了如何配置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>

    <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 bean 已配置了AnnotationJmxAttributeSource class 的实例,并通过汇编程序 property 传递给MBeanExporter。这就是为 Spring-exposed MBeans 利用 metadata-driven management 接口所需的全部内容。

31.3.3 Source-level 元数据类型

以下源 level 元数据类型可用于 Spring JMX:

表格 1_.Source-level 元数据类型

目的注解注释类型
Class的所有实例标记为 JMX 托管资源@ManagedResource
将方法标记为 JMX 操作@ManagedOperation方法
将 getter 或 setter 标记为 JMX 属性的一半@ManagedAttribute方法(只有 getter 和 setter)
定义操作参数的描述@ManagedOperationParameter@ManagedOperationParameters方法

以下 configuration 参数可用于这些 source-level 元数据类型:

表格 1_.Source-level 元数据参数

参数描述适用于
ObjectNameMetadataNamingStrategy用于确定受管资源的ObjectNameManagedResource
description设置资源,属性或操作的友好描述ManagedResource , ManagedAttribute , ManagedOperation , ManagedOperationParameter
currencyTimeLimit设置currencyTimeLimit描述符字段的 valueManagedResource , ManagedAttribute
defaultValue设置defaultValue描述符字段的 valueManagedAttribute
log设置log描述符字段的 valueManagedResource
logFile设置logFile描述符字段的 valueManagedResource
persistPolicy设置persistPolicy描述符字段的 valueManagedResource
persistPeriod设置persistPeriod描述符字段的 valueManagedResource
persistLocation设置persistLocation描述符字段的 valueManagedResource
persistName设置persistName描述符字段的 valueManagedResource
name设置操作参数的 display nameManagedOperationParameter
index设置操作参数的索引ManagedOperationParameter

31.3.4 AutodetectCapableMBeanInfoAssembler 接口

为了进一步简化 configuration,Spring 引入了扩展MBeanInfoAssembler接口的AutodetectCapableMBeanInfoAssembler接口,以添加对 MBean 资源自动检测的支持。如果使用AutodetectCapableMBeanInfoAssembler的实例配置MBeanExporter,则允许对包含 beans 以暴露给 JMX 进行“投票”。

开箱即用,AutodetectCapableMBeanInfo接口的唯一 implementation 是MetadataMBeanInfoAssembler,它将投票包括任何用ManagedResource属性标记的 bean。在这种情况下,默认方法是使用 bean name 作为ObjectName,这会产生如下的 configuration:

<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>

请注意,在此 configuration 中没有 beans 传递给MBeanExporter;但是,JmxTestBean仍将被注册,因为它标有ManagedResource属性,而MetadataMBeanInfoAssembler检测到这一点并投票包含它。这种方法的唯一问题是JmxTestBean的 name 现在具有商业意义。您可以通过更改第 31.4 节,“控制 beans 的 ObjectNames”中定义的ObjectName创建的默认行为来解决此问题。

31.3.5 使用 Java 接口定义 management 接口

除了MetadataMBeanInfoAssembler之外,Spring 还包含InterfaceBasedMBeanInfoAssembler,它允许您根据接口集合中定义的方法集约束公开的方法和 properties。

虽然公开 MBean 的标准机制是使用接口和简单的命名 scheme,但InterfaceBasedMBeanInfoAssembler通过消除对命名约定的需要来扩展此功能,允许您使用多个接口并且无需 beans 来实现 MBean 接口。

考虑这个接口,用于为前面看到的JmxTestBean class 定义 management 接口:

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 上的操作和属性公开的方法和 properties。下面的 code 显示了如何配置 Spring JMX 以使用此接口作为 management 接口的定义:

<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 构造 management 接口时使用IJmxTestBean接口。重要的是要理解InterfaceBasedMBeanInfoAssembler处理的 beans 不需要实现用于生成 JMX management 接口的接口。

在上面的例子中,IJmxTestBean接口用于构造所有 beans 的所有 management 接口。在许多情况下,这不是所需的行为,您可能希望为不同的 beans 使用不同的接口。在这种情况下,您可以通过interfaceMappings property 传递InterfaceBasedMBeanInfoAssembler Properties实例,其中每个条目的 key 是 bean name,每个条目的 value 是用于该 bean 的接口名称列表。

如果没有通过managedInterfacesinterfaceMappings properties 指定 management 接口,则InterfaceBasedMBeanInfoAssembler将反映在 bean 上,并使用该 bean 实现的所有接口来创建 management 接口。

31.3.6 使用 MethodNameBasedMBeanInfoAssembler

MethodNameBasedMBeanInfoAssembler允许您指定将作为属性和操作公开给 JMX 的方法名称列表。下面的 code 显示了一个 sample configuration:

<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>

在这里,您可以看到方法addmyOperation将作为 JMX 操作公开,而getName()setName(String)getAge()将作为 JMX 属性的适当一半公开。在上面的 code 中,方法映射适用于暴露给 JMX 的 beans。要在 bean-by-bean 基础上控制方法曝光,请使用MethodNameMBeanInfoAssembler

31.4 控制 beans 的 ObjectNames

在幕后,MBeanExporter委托实现ObjectNamingStrategy,以便为正在注册的每个 beans 获取ObjectName。默认情况下,_实现KeyNamingStrategy将使用beans Map的 key 作为ObjectName。此外,KeyNamingStrategy可以将beans Map的 key 映射到Properties文件(或 files)中的条目以解析ObjectName。除了KeyNamingStrategy之外,Spring 还提供了两个额外的ObjectNamingStrategy __mplement:IdentityNamingStrategy基于 bean 的 JVM 标识构建ObjectNameMetadataNamingStrategy使用源 level 元数据来获取ObjectName

31.4.1 从 Properties 读取 ObjectNames

您可以配置自己的KeyNamingStrategy实例并将其配置为从Properties实例读取ObjectName而不是使用 bean key。 KeyNamingStrategy将尝试使用与 bean key 对应的 key 在Properties中找到一个条目。如果未找到任何条目或Properties实例为null,则使用 bean key 本身。

下面的 code 显示了KeyNamingStrategy的 sample configuration:

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="testBean" value-ref="testBean"/>
            </map>
        </property>
        <property name="namingStrategy" ref="namingStrategy"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

    <bean id="namingStrategy" class="org.springframework.jmx.export.naming.KeyNamingStrategy">
        <property name="mappings">
            <props>
                <prop key="testBean">bean:name=testBean1</prop>
            </props>
        </property>
        <property name="mappingLocations">
            <value>names1.properties,names2.properties</value>
        </property>
    </bean>

</beans>

这里KeyNamingStrategy的一个实例配置了一个Properties实例,它与映射 property 定义的Properties实例和位于映射 property 定义的 paths 中的 properties files 合并。在此 configuration 中,testBean bean 将被赋予ObjectName bean:name=testBean1,因为这是Properties实例中具有与 bean key 对应的 key 的条目。

如果找不到Properties实例中的条目,则 bean key name 用作ObjectName

31.4.2 使用 MetadataNamingStrategy

MetadataNamingStrategy在每个 bean 上使用ManagedResource属性的objectName property 来创建ObjectName。下面的 code 显示了MetadataNamingStrategy的 configuration:

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="testBean" value-ref="testBean"/>
            </map>
        </property>
        <property name="namingStrategy" ref="namingStrategy"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

    <bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
        <property name="attributeSource" ref="attributeSource"/>
    </bean>

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

</beans>

如果没有为ManagedResource属性提供objectName,则将使用以下格式创建ObjectName:[297] :type = [298], name = [299]。对于 example,以下 bean 生成的ObjectName将为:com.foo:type=MyClass,name=myBean。

<bean id="myBean" class="com.foo.MyClass"/>

31.4.3 配置基于注释的 MBean export

如果您更喜欢使用基于注释的方法来定义 management 接口,那么MBeanExporter的便利子类可用:AnnotationMBeanExporter。定义此子类的实例时,不再需要namingStrategyassemblerattributeSource configuration,因为它将始终使用标准 Java annotation-based 元数据(也始终启用自动检测)。实际上,@EnableMBeanExport 注释支持更简单的语法,而不是定义MBeanExporter bean。

@Configuration
@EnableMBeanExport
public class AppConfig {

}

如果您更喜欢基于 XML 的 configuration,则'context:mbean-export'元素具有相同的用途。

<context:mbean-export/>

如有必要,您可以为特定 MBean server提供 reference,defaultDomain属性(AnnotationMBeanExporter的 property)接受生成的 MBean“ObjectNames”域的备用 value。这将用于代替类 MetadataNamingStrategy上一节中描述的完全限定的包 name。

@EnableMBeanExport(server="myMBeanServer", defaultDomain="myDomain")
@Configuration
ContextConfiguration {

}
<context:mbean-export server="myMBeanServer" default-domain="myDomain"/>

不要将 interface-based AOP 代理与 bean classes 中的 JMX annotations 自动检测结合使用。 Interface-based proxies'隐藏'目标 class,它还隐藏了 JMX 托管资源 annotations。因此,在这种情况下使用 target-class 代理:通过在<aop:config/><tx:annotation-driven/>等上设置'proxy-target-class'flag。否则,您的 JMX beans 可能会在启动时被忽略...

31.5 JSR-160 连接器

对于 remote 访问,Spring JMX 模块在org.springframework.jmx.support包中提供两个FactoryBean implementations,用于创建 server-和 client-side 连接器。

31.5.1 Server-side 个连接器

要让 Spring JMX 创建,启动并公开 JSR-160 JMXConnectorServer,请使用以下 configuration:

<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"/>

默认情况下ConnectorServerFactoryBean创建JMXConnectorServer绑定到"service:jmx:jmxmp://localhost:9875"serverConnector bean 因此通过 localhost,port 9875 上的 JMXMP 协议公开本地MBeanServer到 clients。注意,JSR 160 规范将 JMXMP 协议标记为可选:当前,主 open-source JMX implementation,MX4J 和提供的一个 JDK 不支持 JMXMP。

要指定另一个 URL 并使用MBeanServer注册JMXConnectorServer本身,请分别使用serviceUrlObjectName properties:

<bean id="serverConnector"
        class="org.springframework.jmx.support.ConnectorServerFactoryBean">
    <property name="objectName" value="connector:name=rmi"/>
    <property name="serviceUrl"
            value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector"/>
</bean>

如果ObjectName property 已设置 Spring 将自动注册连接器ObjectName下的MBeanServer。下面的 example 显示了_创建 JMXConnector 时可以传递给ConnectorServerFactoryBean的完整参数集:

<bean id="serverConnector"
        class="org.springframework.jmx.support.ConnectorServerFactoryBean">
    <property name="objectName" value="connector:name=iiop"/>
    <property name="serviceUrl"
        value="service:jmx:iiop://localhost/jndi/iiop://localhost:900/myconnector"/>
    <property name="threaded" value="true"/>
    <property name="daemon" value="true"/>
    <property name="environment">
        <map>
            <entry key="someKey" value="someValue"/>
        </map>
    </property>
</bean>

请注意,使用 RMI-based 连接器时,需要在 order 中启动查找服务(tnameserv 或 rmiregistry)才能完成 name 注册。如果您通过 RMI 使用 Spring 来 export remote 服务,那么 Spring 已经构建了一个 RMI 注册表。如果没有,您可以使用以下 configuration 片段轻松启动注册表:

<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
    <property name="port" value="1099"/>
</bean>

31.5.2 Client-side 个连接器

要创建MBeanServerConnection到 remote JSR-160 启用MBeanServer,请使用MBeanServerConnectionFactoryBean,如下所示:

<bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean">
    <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/>
</bean>

31.5.3 JMX 超过 Burlap/Hessian/SOAP

JSR-160 允许 extensions 到 client 和服务器之间进行通信的方式。上面的示例使用 JSR-160 规范(IIOP 和 JRMP)和(可选)JMXMP 所需的强制 RMI-based implementation。通过使用其他提供程序或 JMX implementations(例如MX4J),您可以利用 SOAP,Hessian,Burlap 等协议优先于简单的 HTTP 或 SSL 等协议:

<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean">
    <property name="objectName" value="connector:name=burlap"/>
    <property name="serviceUrl" value="service:jmx:burlap://localhost:9874"/>
</bean>

在上面的例子中,使用了 MX4J 3.0.0;有关更多信息,请参阅官方 MX4J 文档。

31.6 通过代理访问 MBean

Spring JMX 允许您创建 re-route calls 到在本地或 remote MBeanServer中注册的 MBean 的代理。这些代理为您提供了一个标准的 Java 接口,您可以通过它与 MBean 进行交互。下面的 code 显示了如何在本地MBeanServer中为 MBean running 配置代理:

<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
    <property name="objectName" value="bean:name=testBean"/>
    <property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/>
</bean>

在这里,您可以看到为在ObjectNamebean:name=testBean下注册的 MBean 创建了代理。代理将实现的接口集由proxyInterfaces property 控制,并且这些接口上的映射方法和 properties 规则与 MBean 上的操作和属性的规则与InterfaceBasedMBeanInfoAssembler使用的规则相同。

MBeanProxyFactoryBean可以为任何可通过MBeanServerConnection访问的 MBean 创建代理。默认情况下,找到并使用本地MBeanServer,但您可以覆盖它并提供MBeanServerConnection指向 remote MBeanServer以满足指向 remote MBean 的代理:

<bean id="clientConnector"
        class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean">
    <property name="serviceUrl" value="service:jmx:rmi://remotehost:9875"/>
</bean>

<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
    <property name="objectName" value="bean:name=testBean"/>
    <property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/>
    <property name="server" ref="clientConnector"/>
</bean>

在这里,您可以看到我们使用MBeanServerConnectionFactoryBean创建指向 remote 机器的MBeanServerConnection。然后通过server property 将此MBeanServerConnection传递给MBeanProxyFactoryBean。创建的代理将通过此MBeanServerConnection将所有调用转发到MBeanServer

31.7 通知

Spring 的 JMX 产品包括对 JMX 通知的全面支持。

31.7.1 为通知注册 listeners

Spring 的 JMX 支持可以很容易地将任意数量的NotificationListeners注册到任意数量的 MBean(这包括 Spring 的MBeanExporter导出的 MBean 和通过其他机制注册的 MBean)。通过 example 的方式,考虑一个场景,其中一个人希望(通过Notification)每个 time 一个目标 MBean 的属性发生变化。

package com.example;

import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;

public class ConsoleLoggingNotificationListener
        implements NotificationListener, NotificationFilter {

    public void handleNotification(Notification notification, Object handback) {
        System.out.println(notification);
        System.out.println(handback);
    }

    public boolean isNotificationEnabled(Notification notification) {
        return AttributeChangeNotification.class.isAssignableFrom(notification.getClass());
    }

}
<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="notificationListenerMappings">
            <map>
                <entry key="bean:name=testBean1">
                    <bean class="com.example.ConsoleLoggingNotificationListener"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

通过上面的 configuration 配置,每隔 time JMX Notification从目标 MBean(bean:name=testBean1)broadcast,ConsoleLoggingNotificationListener bean 将通过notificationListenerMappings property 注册为 listener。然后ConsoleLoggingNotificationListener bean 可以采取它认为适当的任何行动来响应Notification

您还可以使用直 bean 名称作为导出的 beans 和 listeners 之间的链接:

<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="notificationListenerMappings">
            <map>
                <entry key="testBean">
                    <bean class="com.example.ConsoleLoggingNotificationListener"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

如果想要为封闭MBeanExporter导出的所有 beans 注册单个NotificationListener实例,可以使用特殊通配符'*'(无引号)作为notificationListenerMappings property map 中条目的 key;例如:

<property name="notificationListenerMappings">
    <map>
        <entry key="*">
            <bean class="com.example.ConsoleLoggingNotificationListener"/>
        </entry>
    </map>
</property>

如果需要执行逆操作(即,针对 MBean 注册多个不同的 listeners),则必须使用notificationListeners list property(而不是notificationListenerMappings property)。这个 time,而不是简单地为一个 MBean 配置一个NotificationListener,一个配置NotificationListenerBean实例......一个NotificationListenerBean封装一个NotificationListenerObjectName(或ObjectNames),它将在MBeanServer中注册。 NotificationListenerBean还封装了许多其他 properties,例如NotificationFilter和可在高级 JMX 通知方案中使用的任意 handback object。

使用NotificationListenerBean实例时的 configuration 与之前提供的内容没有太大的不同:

<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="notificationListeners">
            <list>
                <bean class="org.springframework.jmx.export.NotificationListenerBean">
                    <constructor-arg>
                        <bean class="com.example.ConsoleLoggingNotificationListener"/>
                    </constructor-arg>
                    <property name="mappedObjectNames">
                        <list>
                            <value>bean:name=testBean1</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

</beans>

上面的 example 相当于第一个通知 example。让我们假设我们希望每_一次被提出一个 handback object,另外我们想要通过提供NotificationFilter来过滤掉无关的Notifications。 (关于 handback object 是什么的完整讨论,以及NotificationFilter究竟是什么,请参考 JMX 规范(1.2)中标题为'JMX Notification Model'的部分.)

<beans>

    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="beans">
            <map>
                <entry key="bean:name=testBean1" value-ref="testBean1"/>
                <entry key="bean:name=testBean2" value-ref="testBean2"/>
            </map>
        </property>
        <property name="notificationListeners">
            <list>
                <bean class="org.springframework.jmx.export.NotificationListenerBean">
                    <constructor-arg ref="customerNotificationListener"/>
                    <property name="mappedObjectNames">
                        <list>
                            <!-- handles notifications from two distinct MBeans -->
                            <value>bean:name=testBean1</value>
                            <value>bean:name=testBean2</value>
                        </list>
                    </property>
                    <property name="handback">
                        <bean class="java.lang.String">
                            <constructor-arg value="This could be anything..."/>
                        </bean>
                    </property>
                    <property name="notificationFilter" ref="customerNotificationListener"/>
                </bean>
            </list>
        </property>
    </bean>

    <!-- implements both the NotificationListener and NotificationFilter interfaces -->
    <bean id="customerNotificationListener" class="com.example.ConsoleLoggingNotificationListener"/>

    <bean id="testBean1" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>

    <bean id="testBean2" class="org.springframework.jmx.JmxTestBean">
        <property name="name" value="ANOTHER TEST"/>
        <property name="age" value="200"/>
    </bean>

</beans>

31.7.2 发布通知

Spring 不仅支持注册接收Notifications,还支持发布Notifications

请注意,此部分实际上仅与通过MBeanExporter公开为 MBean 的 Spring 托管 beans 相关;任何现有的 user-defined MBean 都应使用标准 JMX API 进行通知发布。

Spring 的 JMX 通知发布支持中的 key 接口是NotificationPublisher接口(在org.springframework.jmx.export.notification包中定义)。任何将通过MBeanExporter实例导出为 MBean 的 bean 都可以实现相关的NotificationPublisherAware接口以获取对NotificationPublisher实例的访问权限。 NotificationPublisherAware接口只是通过一个简单的 setter 方法为实现 bean 提供一个NotificationPublisher的实例,然后 bean 可以用来发布Notifications

正如NotificationPublisher class 的 javadocs 中所述,通过NotificationPublisher机制发布 events 的托管 beans 不对任何通知 listeners 等的 state management 负责...... Spring 的 JMX 支持将负责处理所有 JMX 基础结构问题。所有人都需要做一个 application 开发人员实现NotificationPublisherAware接口并开始使用提供的NotificationPublisher实例发布 events。请注意,将在使用MBeanServer注册托管 bean 之后设置。

使用NotificationPublisher实例非常简单......只需创建一个 JMX Notification实例(或适当的Notification子类的实例),使用与要发布的 event 相关的数据填充通知,然后调用sendNotification(Notification) NotificationPublisher实例,传入Notification

在下面找到一个简单的 example ...在这种情况下,JmxTestBean的导出实例将在每次调用add(int, int)操作时发布NotificationEvent

package org.springframework.jmx;

import org.springframework.jmx.export.notification.NotificationPublisherAware;
import org.springframework.jmx.export.notification.NotificationPublisher;
import javax.management.Notification;

public class JmxTestBean implements IJmxTestBean, NotificationPublisherAware {

    private String name;
    private int age;
    private boolean isSuperman;
    private NotificationPublisher publisher;

    // other getters and setters omitted for clarity

    public int add(int x, int y) {
        int answer = x + y;
        this.publisher.sendNotification(new Notification("add", this, 0));
        return answer;
    }

    public void dontExposeMe() {
        throw new RuntimeException();
    }

    public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
        this.publisher = notificationPublisher;
    }

}

NotificationPublisher接口和使其全部工作的机制是 Spring 的 JMX 支持的更好的 features 之一。然而,它确实带有将 classes 与 Spring 和 JMX 耦合的价格标签;和往常一样,这里的建议是务实的......如果你需要NotificationPublisher提供的功能,你可以接受与 Spring 和 JMX 的耦合,那么就这样做。

31.8 更多资源

本节包含有关 JMX 的更多资源的链接。

Updated at: 7 months ago
30.7. JMS 命名空间支持Table of content32. JCA CCI