31. JMX

31.1 简介

Spring中的JMX支持为您提供了将Spring应用程序轻松透明地集成到JMX基础结构中的功能。


JMX?

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


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

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

  • 一种灵活的机制,用于控制bean的管理界面

  • MBean通过远程JSR-160连接器的声明性暴露

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

这些功能旨在在不将应用程序组件耦合到Spring或JMX接口和类的情况下工作。实际上,在大多数情况下,为了利用Spring JMX功能,您的应用程序类无需了解Spring或JMX。

31.2 将bean导出到JMX

Spring的JMX框架中的核心类是 MBeanExporter 。该类负责获取Spring bean并使用JMX MBeanServer 注册它们。例如,请考虑以下类:

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的属性和方法公开为MBean的属性和操作,只需在配置文件中配置 MBeanExporter 类的实例,并传入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>

上面的配置代码段中的相关bean定义是 exporter bean。 beans 属性告诉 MBeanExporter 确切地说哪些bean必须导出到JMX MBeanServer 。在默认配置中, beans Map 中每个条目的键用作相应条目值引用的bean的 ObjectName 。可以按 Section 31.4, “Controlling the ObjectNames for your beans” 中所述更改此行为。

使用此配置, testBean bean在 ObjectName bean:name=testBean1 下公开为MBean。默认情况下,bean的所有公共属性都作为属性公开,所有公共方法(从 Object 类继承的那些公共方法)都作为操作公开。

MBeanExporter是一个Lifecycle bean(请参阅“启动和关闭回调”一节),默认情况下,MBean在应用程序生命周期中尽可能晚地导出。可以通过设置autoStartup标志来配置导出发生的阶段或禁用自动注册。

31.2.1 创建MBeanServer

上面的配置假定应用程序正在一个(并且只有一个) MBeanServer 已在运行的环境中运行。在这种情况下,Spring将尝试找到正在运行的 MBeanServer 并使用该服务器注册您的bean(如果有的话)。当您的应用程序在具有自己的 MBeanServer 的Tomcat或IBM WebSphere等容器中运行时,此行为很有用。

但是,这种方法在独立环境中没有用,或者在不提供 MBeanServer 的容器内运行时。要解决此问题,您可以通过在配置中添加 org.springframework.jmx.support.MBeanServerFactoryBean 类的实例以声明方式创建 MBeanServer 实例。您还可以通过将 MBeanExporterserver 属性的值设置为 MBeanServerFactoryBean 返回的 MBeanServer 值来确保使用特定的 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 创建,并通过server属性提供给 MBeanExporter 。当您提供自己的 MBeanServer 实例时, MBeanExporter 将不会尝试查找正在运行的 MBeanServer 并将使用提供的 MBeanServer 实例。为了正常工作,你必须(当然)在你的类路径上有一个JMX实现。

31.2.2 重用现有的MBeanServer

如果未指定服务器,则 MBeanExporter 会尝试自动检测正在运行的 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>

对于现有 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 懒惰初始化的MBean

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

31.2.4 自动注册MBean

通过 MBeanExporter 导出并且已经是有效MBean的任何bean都按 MBeanServer 注册,而无需Spring的进一步干预。通过将 autodetect 属性设置为 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注册自动检测的bean将其bean名称用作 ObjectName 。可以在 Section 31.4, “Controlling the ObjectNames for your beans” 中详细说明此行为。

31.2.5 控制注册行为

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

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

Table 31.1. Registration Behaviors

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

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

以下示例说明如何实现从默认注册行为到 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 控制bean的管理界面

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

31.3.1 MBeanInfoAssembler接口

在幕后, MBeanExporter 委托给 org.springframework.jmx.export.assembler.MBeanInfoAssembler 接口的实现,该接口负责定义正在公开的每个bean的管理接口。默认实现 org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler 只是定义了一个公开所有公共属性和方法的管理接口(如前面的例子所示)。 Spring提供了 MBeanInfoAssembler 接口的两个附加实现,允许您使用源级元数据或任意任意控制生成的管理接口接口。

31.3.2 使用源级元数据:Java注释

使用 MetadataMBeanInfoAssembler ,您可以使用源级元数据为Bean定义管理接口。元数据的读取由 org.springframework.jmx.export.metadata.JmxAttributeSource 接口封装。 Spring JMX提供了一个使用Java注释的默认实现,即 org.springframework.jmx.export.annotation.AnnotationJmxAttributeSourceMetadataMBeanInfoAssembler 必须配置 JmxAttributeSource 接口的实现实例才能正常运行(没有默认值)。

要标记要导出到JMX的bean,应使用 ManagedResource 批注对bean类进行批注。您希望作为操作公开的每个方法都必须使用 ManagedOperation 注释进行标记,并且您希望公开的每个属性都必须使用 ManagedAttribute 注释进行标记。标记属性时,可以省略getter或setter的注释,以分别创建只写或只读属性。

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

下面的示例显示了您之前看到的 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 注释,并且此 ManagedResource 注释配置了一组属性。这些属性可用于配置 MBeanExporter 生成的MBean的各个方面,稍后将在 Headers 为 Section 31.3.3, “Source-level metadata types” 的部分中进行更详细的说明。

您还会注意到 agename 属性都使用 ManagedAttribute 注释进行了注释,但在 age 属性的情况下,只标记了getter。这将导致这两个属性作为属性包含在管理界面中,但 age 属性将是只读的。

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

以下配置显示了如何配置 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 类的实例,并通过汇编程序属性传递给 MBeanExporter 。这就是利用面向Spring的MBean的元数据驱动管理接口所需的全部内容。

31.3.3 源级元数据类型

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

Table 31.2. Source-level metadata types

用途注释注释类型
Class 的所有实例标记为JMX托管资源@ManagedResource
将方法标记为JMX操作@ManagedOperation方法
将getter或setter标记为JMX属性的一半@ManagedAttribute方法(仅限getter和setter)
定义操作参数的说明@ManagedOperationParameter@ManagedOperationParameters方法

以下配置参数可用于这些源级元数据类型:

Table 31.3. Source-level metadata parameters

参数描述适用于
ObjectNameMetadataNamingStrategy 用于确定托管资源的 ObjectNameManagedResource
description设置资源,属性或操作的友好描述ManagedResourceManagedAttributeManagedOperationManagedOperationParameter
currencyTimeLimit设置 currencyTimeLimit 描述符字段的值ManagedResourceManagedAttribute
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的索引

31.3.4 AutodetectCapableMBeanInfoAssembler接口

为了进一步简化配置,Spring引入了 AutodetectCapableMBeanInfoAssembler 接口,该接口扩展了 MBeanInfoAssembler 接口,以增加对MBean资源自动检测的支持。如果使用 AutodetectCapableMBeanInfoAssembler 的实例配置 MBeanExporter ,则允许 "vote" 包含 beans 类以接触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="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 的名称现在具有商业含义。您可以通过更改 Section 31.4, “Controlling the ObjectNames for your beans” 中定义的 ObjectName 创建的默认行为来解决此问题。

31.3.5 使用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的以逗号分隔的接口名称列表。

如果没有通过 managedInterfacesinterfaceMappings 属性指定管理接口,则 InterfaceBasedMBeanInfoAssembler 将反映bean并使用该bean实现的所有接口来创建管理接口。

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

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

31.4 控制bean的ObjectNames

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

31.4.1 从属性中读取ObjectNames

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

下面的代码显示了 KeyNamingStrategy 的示例配置:

<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 实例,该实例与映射属性定义的 Properties 实例和位于由映射属性定义的路径中的属性文件合并。在此配置中, testBean bean将被赋予 ObjectName bean:name=testBean1 ,因为这是 Properties 实例中具有与bean键对应的键的条目。

如果没有条目可以找到 Properties 实例,然后将bean键名称用作 ObjectName

31.4.2 使用MetadataNamingStrategy

MetadataNamingStrategy 使用每个bean上 ManagedResource 属性的 objectName 属性来创建 ObjectName 。下面的代码显示了 MetadataNamingStrategy 的配置:

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

如果没有 objectName 已提供用于 ManagedResource 属性,那么 ObjectName 将具有以下格式创建的:[完全合格的包名称]:类型= [短类名],名称= [ beans 名称]。例如,以下bean生成的 ObjectName 将是:com.foo:type = MyClass,name = myBean。

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

31.4.3 配置基于MBean导出的注释

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

@Configuration
@EnableMBeanExport
public class AppConfig {

}

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

<context:mbean-export/>

如有必要,您可以提供对特定MBean server 的引用, defaultDomain 属性( AnnotationMBeanExporter 的属性)接受生成的MBean“ObjectNames”域的备用值。这将用于代替完整限定的包名称,如上一节 MetadataNamingStrategy 中所述。

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

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

不要将基于接口的AOP代理与bean类中JMX注释的自动检测结合使用。基于接口的代理“隐藏”目标类,它还隐藏了JMX托管资源注释。因此,在这种情况下使用目标类代理:通过在<aop:config />,<tx:annotation-driven />等设置'proxy-target-class'标志,否则,您的JMX bean可能会被忽略在启动时......

31.5 JSR-160连接器

对于远程访问,Spring JMX模块在 org.springframework.jmx.support 包中提供了两个 FactoryBean 实现,用于创建服务器端和客户端连接器。

31.5.1 服务器端连接器

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

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

默认情况下 ConnectorServerFactoryBean 创建 JMXConnectorServer 绑定到 "service:jmx:jmxmp://localhost:9875" 。因此, serverConnector bean公开本地 MBeanServer 到客户端通过在本地主机的JMXMP协议,端口9875.注意,JMXMP协议由JSR 160规范标记为可选:目前,主要的开源JMX实现,MX4J,并且所述一个JDK提供的不支持JMXMP。

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

<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 属性,Spring将自动注册连接器 ObjectName 下的 MBeanServer 。下面的示例显示了在创建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的连接器时,需要启动查找服务(tnameserv或rmiregistry)才能完成名称注册。如果您使用Spring通过RMI为您导出远程服务,那么Spring已经构建了一个RMI注册表。如果没有,您可以使用以下配置片段轻松启动注册表:

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

31.5.2 客户端连接器

要创建 MBeanServerConnection 到启用远程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 over Burlap / Hessian / SOAP

JSR-160允许扩展客户端和服务器之间的通信方式。上面的示例使用JSR-160规范(IIOP和JRMP)和(可选)JMXMP所需的基于RMI的强制实现。通过使用其他提供程序或JMX实现(例如 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允许您创建代理,将代理重新路由到在本地或远程 MBeanServer 中注册的MBean。这些代理为您提供了一个标准的Java接口,您可以通过它与MBean进行交互。下面的代码显示了如何为在本地 MBeanServer 中运行的MBean配置代理:

<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 属性控制,并且将这些接口上的方法和属性映射到MBean上的操作和属性的规则是 InterfaceBasedMBeanInfoAssembler 使用的规则相同。

MBeanProxyFactoryBean 可以为任何可通过 MBeanServerConnection 访问的MBean创建代理。默认情况下,找到并使用本地 MBeanServer ,但是您可以覆盖它并提供指向远程 MBeanServerMBeanServerConnection 以满足指向远程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 创建指向远程计算机的 MBeanServerConnection 。然后 MBeanServerConnection 通过 server 属性传递给 MBeanProxyFactoryBean 。创建的代理将通过此 MBeanServerConnection 将所有调用转发给 MBeanServer

31.7 通知

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

31.7.1 注册通知的侦听器

Spring的JMX支持使用任意数量的MBean注册任意数量的 NotificationListeners 变得非常容易(这包括由Spring的 MBeanExporter 导出的MBean和通过其他机制注册的MBean)。举一个例子,考虑一个场景,即每当目标MBean的一个属性发生变化时,就会通知(通过 Notification )。

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>

通过上述配置,每次从目标MBean( bean:name=testBean1 )广播JMX Notification 时,将通知通过 notificationListenerMappings 属性注册为侦听器的 ConsoleLoggingNotificationListener bean。然后 ConsoleLoggingNotificationListener bean可以采取它认为适当的任何操作来响应 Notification

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

<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 导出的所有bean注册单个 NotificationListener 实例,可以使用特殊通配符 '*' (无引号)作为 notificationListenerMappings 属性映射中的条目的键;例如:

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

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

使用 NotificationListenerBean 实例时的配置与之前的实例没有太大的不同:

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

上面的示例等同于第一个通知示例。让我们假设我们希望每次引发 Notification 时都给予一个handback对象,另外我们想要通过提供 NotificationFilter 来过滤掉无关的 Notifications 。 (有关回传对象的完整讨论,以及 NotificationFilter 的确实是什么,请参考 Headers 为'JMX通知模型'的JMX规范(1.2)部分。)

<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托管bean相关;任何现有的,用户定义的MBean都应使用标准JMX API进行通知发布。

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

正如 NotificationPublisher 类的javadoc中所述,通过 NotificationPublisher 机制发布事件的托管bean不负责任何通知侦听器的状态管理等... Spring的JMX支持将负责处理所有JMX基础结构问题。所有人都需要这样做,因为应用程序开发人员实现 NotificationPublisherAware 接口并使用提供的 NotificationPublisher 实例开始发布事件。请注意,在使用 MBeanServer 注册托管bean之后,将设置 NotificationPublisher

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

在下面找到一个简单的示例...在这种情况下,每次调用 add(int, int) 操作时, JmxTestBean 的导出实例将发布 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支持的一个更好的功能。然而,它确实带有将类与Spring和JMX耦合的价格标签;和往常一样,这里的建议是务实的......如果你需要 NotificationPublisher 提供的功能,你可以接受与Spring和JMX的耦合,那么就这样做。

31.8 更多资源

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

Updated at: 5 months ago
30.7. JMS命名空间支持Table of content32. JCA CCI
Comment
You are not logged in.

There are no comments.