On this page
31. JMX
31.1 Introduction
Spring 中的 JMX 支持为您提供了轻松,透明地将 Spring 应用程序集成到 JMX 基础结构中的功能。
JMX?
本章不是 JMX 的介绍……它不会试图解释为什么可能要使用 JMX 的动机(或者实际上是 JMX 代表的字母)。如果您不熟悉 JMX,请在本章末尾查看第 31.8 节“其他资源”。
具体来说,Spring 的 JMX 支持提供了四个核心功能:
将* any * Spring bean 自动注册为 JMX MBean
一种灵活的机制,用于控制 bean 的 Management 界面
通过远程 JSR-160 连接器以声明方式公开 MBean
本地和远程 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
。可以按照第 31.4 节“控制 bean 的对象名”中所述更改此行为。
通过这种配置,testBean
bean 作为ObjectName
bean:name=testBean1
下的 MBean 公开。默认情况下,bean 的所有* public 属性都作为属性公开,而所有 public *方法(从Object
类继承的方法除外)都作为操作公开。
Note
MBeanExporter
是Lifecycle
bean(请参见名为“启动和关闭回调”的部分),默认情况下,MBean 在应用程序生命周期中尽可能晚地导出。通过设置autoStartup
标志,可以配置发生导出的phase
或禁用自动注册。
31.2.1 创建 MBeanServer
上面的配置假设应用程序正在一个(并且只有一个)MBeanServer
已运行的环境中运行。在这种情况下,Spring 将尝试找到正在运行的MBeanServer
并将您的 bean 注册到该服务器(如果有)。当您的应用程序在具有自己的MBeanServer
的容器(例如 Tomcat 或 IBM WebSphere)中运行时,此行为很有用。
但是,此方法在独立环境中或在不提供MBeanServer
的容器中运行时无用。为了解决这个问题,您可以通过在配置中添加org.springframework.jmx.support.MBeanServerFactoryBean
类的实例来声明性地创建MBeanServer
实例。您还可以通过将MBeanExporter
的server
属性的值设置为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
实例。为了使其正常工作,您必须(当然)必须在 Classpath 上具有 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
将不会违反此 Contract,并且将避免实例化该 Bean。相反,它将在MBeanServer
处注册一个代理,并将从容器中获取 Bean,直到对该代理进行第一次调用为止。
31.2.4 MBean 的自动注册
通过MBeanExporter
导出并且已经是有效 MBean 的所有 bean 都将直接在MBeanServer
上注册,而无需 Spring 的进一步干预。通过将autodetect
属性设置为true
,MBeanExporter
可以自动检测 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
。如第 31.4 节“控制 bean 的对象名”中所述,可以覆盖此行为。
31.2.5 控制注册行为
考虑以下情形:Spring MBeanExporter
尝试使用ObjectName
'bean:name=testBean1'
向MBeanServer
注册MBean
。如果MBean
实例已经在同一ObjectName
下注册,则默认行为是失败(并抛出InstanceAlreadyExistsException
)。
当MBean
注册到MBeanServer
时,可以精确控制行为。当注册过程发现MBean
已经在同一ObjectName
下注册时,Spring 的 JMX 支持允许三种不同的注册行为来控制注册行为。下表总结了这些注册行为:
表 31.1. 注册行为
Registration behavior | Explanation |
---|---|
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 的 Management 界面
在前面的示例中,您几乎无法控制 Bean 的 Management 界面。每个导出 bean 的* public 属性和方法的所有分别公开为 JMX 属性和操作。为了对已导出的 bean 的哪些属性和方法实际上作为 JMX 属性和操作公开而进行更细粒度的控制,Spring JMX 提供了一种全面且可扩展的机制来控制 bean 的 Management 接口。
31.3.1 MBeanInfoAssembler 接口
在幕后,MBeanExporter
委托org.springframework.jmx.export.assembler.MBeanInfoAssembler
接口的实现,该接口负责定义要公开的每个 bean 的 Management 接口。默认实现org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler
只是定义了一个 Management 接口,该接口公开了所有公共属性和方法(如前面的示例所示)。 Spring 提供了MBeanInfoAssembler
接口的两个附加实现,允许您使用源级元数据或任何任意接口来控制生成的 Management 接口。
31.3.2 使用源级元数据:Java 注解
使用MetadataMBeanInfoAssembler
,可以使用源级元数据为 bean 定义 Management 界面。元数据的读取由org.springframework.jmx.export.metadata.JmxAttributeSource
接口封装。 Spring JMX 提供了一个使用 JavaComments 的默认实现,即org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource
。必须为MetadataMBeanInfoAssembler
配置JmxAttributeSource
接口的实现实例,才能使其正常运行(默认为* no *)。
要标记要导出到 JMX 的 bean,应使用ManagedResource
Comments 对 bean 类进行 Comments。您希望作为操作公开的每个方法都必须带有ManagedOperation
注解,并且您希望公开的每个属性都必须带有ManagedAttribute
注解。标记属性时,可以省略 getter 或 setter 的 Comments,以分别创建只写或只读属性。
Note
带有ManagedResource
Comments 的 Bean 以及公开操作或属性的方法都必须是公共的。
下面的示例显示了您先前看到的JmxTestBean
类的带 Comments 的版本:
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
Comments,并且此ManagedResource
Comments 配置了一组属性。这些属性可用于配置MBeanExporter
生成的 MBean 的各个方面,稍后在标题为第 31.3.3 节“源级元数据类型”的部分中将进行详细说明。
您还将注意到,age
和name
属性都用ManagedAttribute
Comments 进行了 Comments,但是对于age
属性,仅标记了吸气剂。这将导致这两个属性都作为属性包含在 Management 界面中,但是age
属性将是只读的。
最后,您会注意到add(int, int)
方法标记有ManagedOperation
属性,而dontExposeMe()
方法未标记。使用MetadataMBeanInfoAssembler
时,这将导致 Management 界面仅包含一个操作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 利用元数据驱动的 Management 接口所需要的全部。
31.3.3 源级元数据类型
在 Spring JMX 中可以使用以下源级别的元数据类型:
表 31.2 源级元数据类型
Purpose | Annotation | Annotation Type |
---|---|---|
将Class 的所有实例标记为 JMX 托管资源 |
@ManagedResource |
Class |
将方法标记为 JMX 操作 | @ManagedOperation |
Method |
将获取器或设置器标记为 JMX 属性的一半 | @ManagedAttribute |
方法(仅 getter 和 setter) |
定义操作参数的描述 | @ManagedOperationParameter 和@ManagedOperationParameters |
Method |
以下配置参数可用于这些源级别的元数据类型:
表 31.3 源级元数据参数
Parameter | Description | Applies to |
---|---|---|
ObjectName |
MetadataNamingStrategy 用于确定托管资源的ObjectName |
ManagedResource |
description |
设置资源,属性或操作的友好描述 | ManagedResource , ManagedAttribute , ManagedOperation , ManagedOperationParameter |
currencyTimeLimit |
设置currencyTimeLimit Descriptors 字段的值 |
ManagedResource , ManagedAttribute |
defaultValue |
设置defaultValue Descriptors 字段的值 |
ManagedAttribute |
log |
设置log Descriptors 字段的值 |
ManagedResource |
logFile |
设置logFile Descriptors 字段的值 |
ManagedResource |
persistPolicy |
设置persistPolicy Descriptors 字段的值 |
ManagedResource |
persistPeriod |
设置persistPeriod Descriptors 字段的值 |
ManagedResource |
persistLocation |
设置persistLocation Descriptors 字段的值 |
ManagedResource |
persistName |
设置persistName Descriptors 字段的值 |
ManagedResource |
name |
设置操作参数的显示名称 | ManagedOperationParameter |
index |
设置操作参数的索引 | ManagedOperationParameter |
31.3.4 AutodetectCapableMBeanInfoAssembler 接口
为了进一步简化配置,Spring 引入了AutodetectCapableMBeanInfoAssembler
接口,该接口扩展了MBeanInfoAssembler
接口以添加对自动检测 MBean 资源的支持。如果用AutodetectCapableMBeanInfoAssembler
的实例配置MBeanExporter
,则可以在包含 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="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
的名称现在具有商业意义。您可以通过更改第 31.4 节“控制 bean 的对象名”中定义的ObjectName
创建的默认行为来解决此问题。
31.3.5 使用 Java 接口定义 Management 接口
除了MetadataMBeanInfoAssembler
之外,Spring 还包括InterfaceBasedMBeanInfoAssembler
,它使您可以基于一组接口中定义的一组方法来约束公开的方法和属性。
尽管公开 MBean 的标准机制是使用接口和简单的命名方案,但是InterfaceBasedMBeanInfoAssembler
扩展了此功能,从而消除了对命名约定的需要,允许您使用多个接口,并且不再需要您的 bean 来实现 MBean 接口。 。
考虑以下接口,该接口用于为您先前看到的JmxTestBean
类定义 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 上作为操作和属性公开的方法和属性。下面的代码显示了如何配置 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>
在这里,您可以看到在为任何 bean 构造 Management 接口时,将InterfaceBasedMBeanInfoAssembler
配置为使用IJmxTestBean
接口。重要的是要理解,对于实现用于生成 JMXManagement 接口的接口,不需要InterfaceBasedMBeanInfoAssembler
处理的 bean。
在上述情况下,IJmxTestBean
接口用于为所有 bean 构造所有 Management 接口。在许多情况下,这不是理想的行为,您可能想对不同的 bean 使用不同的接口。在这种情况下,您可以通过interfaceMappings
属性传递InterfaceBasedMBeanInfoAssembler
一个Properties
实例,其中每个条目的键是 Bean 名称,每个条目的值是一个用逗号分隔的接口名称列表,用于该 Bean。
如果未通过managedInterfaces
或interfaceMappings
属性指定任何 Management 接口,则InterfaceBasedMBeanInfoAssembler
将反映在 Bean 上,并使用该 Bean 实施的所有接口来创建 Management 接口。
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>
在这里,您可以看到方法add
和myOperation
将作为 JMX 操作公开,而getName()
,setName(String)
和getAge()
将作为 JMX 属性的相应一半公开。在上面的代码中,方法 Map 适用于公开给 JMX 的 bean。要控制每个 bean 的方法公开,请使用MethodNameMBeanInfoAssembler
的methodMappings
属性将 bean 名称 Map 到方法名称列表。
31.4 控制 bean 的对象名
在幕后,MBeanExporter
委派ObjectNamingStrategy
的实现,以便为其注册的每个 bean 获得ObjectName
。默认实现KeyNamingStrategy
默认情况下将beans
Map
的键用作ObjectName
。此外,KeyNamingStrategy
可以将beans
Map
的键 Map 到Properties
文件(或多个文件)中的条目以解析ObjectName
。除了KeyNamingStrategy
之外,Spring 还提供了两个附加的ObjectNamingStrategy
实现:IdentityNamingStrategy
基于 bean 的 JVM 身份构建ObjectName
,以及MetadataNamingStrategy
使用源级别元数据获取ObjectName
。
31.4.1 从属性读取对象名称
您可以配置自己的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
实例,该实例从 maping 属性定义的Properties
实例和位于 mappings 属性定义的路径中的属性文件合并而成。在此配置中,testBean
bean 将被赋予ObjectName
bean:name=testBean1
,因为这是Properties
实例中的条目,该条目具有与 bean 密钥相对应的密钥。
如果在Properties
实例中找不到任何条目,则将 bean 键名用作ObjectName
。
31.4.2 使用元数据命名策略
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>
如果没有为ManagedResource
属性提供objectName
,则将使用以下格式创建ObjectName
:* [完全合格的软件包名称]:type = [短类名称],name = [bean 名称] 。例如,为以下 bean 生成的ObjectName
将是: com.foo:type = MyClass,name = myBean *。
<bean id="myBean" class="com.foo.MyClass"/>
31.4.3 配置基于 Comments 的 MBean 导出
如果您更喜欢使用基于 Comments 的方法定义 Management 界面,则可以使用MBeanExporter
的便利子类:AnnotationMBeanExporter
。在定义此子类的实例时,不再需要namingStrategy
,assembler
和attributeSource
配置,因为它将始终使用标准的基于 JavaComments 的元数据(也始终启用自动检测)。实际上,@EnableMBeanExport
@Configuration
Comments 支持一种甚至更简单的语法,而不是定义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"/>
Note
请勿将基于接口的 AOP 代理与 bean 类中的 JMXComments 的自动检测结合使用。基于接口的代理“隐藏”目标类,该类也隐藏了 JMXManagement 的资源 Comments。因此,在这种情况下,请使用目标类代理:通过在<aop:config/>
,<tx:annotation-driven/>
等上设置'proxy-target-class'标志。否则,启动时可能会静默忽略您的 JMX bean…
31.5 JSR-160 连接器
对于远程访问,Spring JMX 模块在org.springframework.jmx.support
包中提供了两个FactoryBean
实现,用于创建服务器端和 Client 端连接器。
31.5.1 服务器端连接器
要创建 Spring JMX,使用以下配置启动和公开 JSR-160 JMXConnectorServer
:
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"/>
默认情况下ConnectorServerFactoryBean
创建绑定到"service:jmx:jmxmp://localhost:9875"
的JMXConnectorServer
。因此,serverConnector
bean 通过 localhost 上的 JMXMP 协议(端口 9875)向 Client 端公开本地MBeanServer
。请注意,JSR 160 规范将 JMXMP 协议标记为可选:当前,主要的开源 JMX 实现 MX4J,还有一个 JDK 随附的不支持 JMXMP。
要指定另一个 URL 并使用MBeanServer
注册JMXConnectorServer
本身,请分别使用serviceUrl
和ObjectName
属性:
<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.2Client 端连接器
要为启用了远程 JSR-160 的MBeanServer
创建MBeanServerConnection
,请使用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 粗麻布/黑森 State/ SOAP 上的 JMX
JSR-160 允许扩展 Client 端与服务器之间进行通信的方式。上面的示例使用 JSR-160 规范(IIOP 和 JRMP)和(可选)JMXMP 所需的基于 RMI 的强制实现。通过使用其他提供程序或 JMX 实现(例如MX4J),您可以通过简单的 HTTP 或 SSL 以及其他协议利用 SOAP,Hessian,Burlap 等协议:
<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>
在这里,您可以看到为在ObjectName
:bean:name=testBean
下注册的 MBean 创建了代理。代理将实现的接口集由proxyInterfaces
属性控制,将这些接口上的方法和属性 Map 到 MBean 上的操作和属性的规则与InterfaceBasedMBeanInfoAssembler
使用的规则相同。
MBeanProxyFactoryBean
可以为可通过MBeanServerConnection
访问的任何 MBean 创建代理。默认情况下,已找到并使用了本地MBeanServer
,但是您可以覆盖它并提供一个指向远程MBeanServer
的MBeanServerConnection
以迎合指向远程 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 Notifications
Spring 的 JMX 产品包括对 JMX 通知的全面支持。
31.7.1 注册侦听器以接收通知
Spring 的 JMX 支持使注册任意数量的NotificationListeners
到任意数量的 MBean 变得非常容易(这包括 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
)BroadcastJMX 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
属性 Map 中条目的键;例如:
<property name="notificationListenerMappings">
<map>
<entry key="*">
<bean class="com.example.ConsoleLoggingNotificationListener"/>
</entry>
</map>
</property>
如果需要进行相反操作(即,针对 MBean 注册许多不同的侦听器),则必须使用notificationListeners
list 属性(并且优先使用notificationListenerMappings
属性)。这次,不是为单个 MBean 简单配置NotificationListener
,而是配置NotificationListenerBean
实例…NotificationListenerBean
封装了NotificationListener
和要在MBeanServer
中注册的ObjectName
(或ObjectNames
)。 NotificationListenerBean
还封装了许多其他属性,例如NotificationFilter
和可以在高级 JMX 通知场景中使用的任意 handback 对象。
使用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
时都希望得到一个递归对象,另外,我们还想通过提供NotificationFilter
来过滤掉无关的Notifications
。 (有关回传对象是什么,实际上NotificationFilter
是什么的完整讨论,请查阅 JMX 规范(1.2)中名为“ JMX 通知模型”的部分。)
<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
的支持。
Note
请注意,本节实际上仅与已通过MBeanExporter
公开为 MBean 的 Spring 托管 Bean 有关。任何现有的用户定义的 MBean 都应使用标准的 JMX API 进行通知发布。
Spring 的 JMX 通知发布支持中的关键接口是NotificationPublisher
接口(在org.springframework.jmx.export.notification
包中定义)。任何要通过MBeanExporter
实例作为 MBean 导出的 bean 都可以实现相关的NotificationPublisherAware
接口来访问NotificationPublisher
实例。 NotificationPublisherAware
接口只是通过一个简单的 setter 方法将NotificationPublisher
的实例提供给实现 Bean,然后该 bean 可以用来发布Notifications
。
如NotificationPublisher
类的 javadocs 中所述,通过NotificationPublisher
机制发布事件的托管 bean 不负责任何通知侦听器等的状态 Management……Spring 的 JMX 支持将负责处理所有 JMX 基础结构问题。作为应用程序开发人员,所有需要做的就是实现NotificationPublisherAware
接口并使用提供的NotificationPublisher
实例开始发布事件。注意,在托管 Bean 已通过MBeanServer
注册之后,将设置NotificationPublisher
。
使用NotificationPublisher
实例非常简单……只需创建一个 JMX Notification
实例(或适当的Notification
子类的实例),然后使用与要发布的事件相关的数据填充通知,然后在sendNotification(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 的更多资源的链接。
甲骨文JMX homepage
JMX specification(JSR-000003)
JMX 远程 API 规范(JSR-000160)
MX4J homepage(各种 JMX 规范的开源实现)