19.1. 简介
Spring的JMX支持所提供的特性使你容易而又透明地将你的Spring应用集成进一个JMX架构中。Spring JMX明确提供了4个核心特性:
这些特性被设计用来使你的应用组件在不必耦合到Spring或JMX接口或类的情况下就可以工作。实际上,你的大部分应用类可以在不需要知道Spring或JMX的情况下使用Spring的JMX特性。
19.2. 导出你的Bean到JMX
Spring JMX框架中的核心类是MbeanExporter. 该类负责获取你的Spring beans并在JMX MbeanServer中注册它们。例如,假设有如下的简单bean类:
要将该bean中的属性和方法公布为JMX Mbean中的参数和操作,你只要在配置文件 中简单的配置MbeanExporter类并传入如下的bean:
这里,最重要的是定义exporter bean. beans属性被用来通知MbeanExporter你有那些beans将被发布到JMX MbeanServer. beans属性是Map类型,因而你能使用<map>和<entry>标记来配置要发布的beans. 在默认的配置中,Map中条目的键值被用作该条目中value所指bean的ObjectName. 这个动作可以按照19.4节"控制你的beans的ObjectName"的描述进行更改.
用这个配置,testBean在ObjectName bean:name=testBean1下被发布为JMX Mbean,。所有的公共属性被公布为参数而所有公共方法(除去在Object中定义的那些)被公布为操作。
19.2.1. 创建一个MBeanServer
如上所示的配置中假定该应用运行在一个有且只有一个MbeanServer正在运行的环境中。在这种情况下,Spring将定位正在运行的MbeanServer并注册你的beans到其中。这在你的应用运行在一个拥有自己的MbeanServer的容器内如Tomcat或者IBM WebSphere时很有用。
然而,这个方法无法用于独立环境中,或者当它运行在一个没有提供MbeanServer的容器时。为了克服这种困难,你可以通过在配置中声明添加一个org.springframework.jmx. support.MbeanServerFactoryBean的实例来创建MbeanServer的实例。通过设置MbeanExporter的server属性为MbeanServerFactoryBean,你同样可以确保这个MbeanServer被使用。如下所示:
这里一个MbeanServer的实例被创建并通过service属性提供给MbeanExporter,当你提供你自己的MbeanServer时,MbeanExporter将不再尝试去访问正在运行的MbeanServer。为了让它正常工作,在你的classpath下必须有一个JMX的实现。
19.2.2. Mbean的延迟加载
如果你配置了一个bean为MbeanExporter,还可以配置它为延迟加载,这时MbeanExporter将不会违反这个约定并避免实例化这个bean. 作为代替,它将为MbeanServer注册一个代理并推迟从BeanFactory获取该bean直到该代理进行第一次调用。
19.2.3. 自动注册MBeans
任何通过MbeanExporter发布的beans和按照现在的样子在MbeanServer中定义的有效Mbeans并不需要Spring进一步介入。通过设置autodetect属性为true,Mbeans能被MBeanExporter自动探测到。
这里命名为spring:mbean=true的bean已经是一个有效的JMX Mbean并自动会被Spring注册。通常,自动探测到的bean用于JMX注册时用它们的bean名字作为ObjectName。这个动作可以按照18.4节"控制你的beans的ObjectName"的描述进行更改.
19.3. 控制你的bean的管理接口
在前面的例子中,你只能通过你的bean的管理接口对所有暴露出来的公共属性和方法进行很少的控制。为解决这个问题,Spring JMX提供一个全面又可扩展的机制来控制你的bean的管理接口。
19.3.1. MbeanInfoAssembler接口
在后台,MbeanExporter委派一个org.springframework.jmx.export. assembler.MbeanInfoAssembler接口的实现来负责为每个bean定义发布出来的管理接口。默认的实现org.springframework.jmx.export.assembler. SimpleReflectiveMBeanInfoAssembler,只是简单的定义一个接口发布所有的公共参数和方法,正如你在前面的例子中看到的一样。Spring为MbeanInfoAssembler接口提供两个额外的实现,允许你通过源码级元数据或任意的接口来控制管理接口。
19.3.2. 使用源码级元数据
利用MetadataMBeanInfoAssembler,你能使用源码级元数据为你的bean定义管理接口。元数据的读取被封装在org.springframework.jmx.export.metadata. JmxAttributeSource接口。在该单元外,Spring JMX为这个接口提供两种支持:支持普通参数的org.springframework.jmx.export.metadata. AttributesJmxAttributeSource和支持JDK5.0注解的org.springframework. jmx.export.annotation.AnnotationJmxAttributeSource。MetadataMBeanInfoAssembler必须要配置一个JmxAttributeSource的实现才能正确运行。在这个例子中,我们将使用普通参数元数据的方法。
要标示一个bean发布给JMX,你应当用ManagedResource参数来注解这个bean类。在使用普通参数元数据方法的场合中,这个类可以在org.springframework.jmx.metadata包中找到。每个要发布为操作的方法应当用ManagedOperation属性来标示,每个要发布的参数应当用ManagedAttribute参数来标示。当标示参数时,你可以省略getter或setter来分别创建一个只写或只读的参数。
下面的例子显示了用普通参数元数据标示前面看到的JmxTestBean类:
这里你可以看到,JmxTestBean类被ManagedResource参数标示并且ManagedResource参数被配置到一个属性集。这些属性能被用于配置由MbeanExporter产生的Mbean的不同的切面,进一步的说明请参见19.3.4 "Source-Level Metadata Types".
你同样注意到age和name属性都用ManagedAttribute来标示,但是age属性只标示了getter方法。这将使得这两个属性都作为参数被包含在管理接口中,并且age参数是只读的。
最后,你会注意到add(int, int)方法用ManagedOperation参数来标示而dontExposeMe()方法没有标示。这将使得当使用MetadataMBeanInfoAssembler时,管理接口中只包含一个操作add(int, int)。
下面的代码展示了如何使用MetadataMBeanInfoAssembler来配置MbeanExporter:
这里你可以看到,一个MetadataMBeanInfoAssembler bean设置了一个AttributesJmxAttributeSource的实例,并且通过assembler属性传递到MbeanExporter中,这就是在你的Spring-exposed Mbeans中利用metadata-driven管理接口时所有的要求。
19.3.3. 使用JDK 5.0的注解
要在管理接口定义中使用JDK5.0的注解,Spring提供了一个注解集镜像到Commons Attribute参数类和一个JMX的实现来支持JmxAttributeSource, AnnotationsJmxAttributeSource并允许MbeanInfoAssembler读取它们。
下面的例子显示了一个用JDK5.0注解定义管理接口的bean:
正如你所见,除了元数据定义的语法外只有很少的变动。这个方法在后台启动时会有一点缓慢,因为要在类中使用Commons Attributes转换JDK5.0的注解。不过,这仅仅是一次性的开销并且JDK5.0的注解为你带来编译时检查的好处。
19.3.4. Source-Level Metadata Types
下面是Spring JMX中使用的源码级元数据类型:
Table 19.1. Source-Level Metadata Types
下面是源码级元数据类型中使用的配置参数:
Table 19.2. Source-Level Metadata Parameters
19.3.5. AutodetectCapableMBeanInfoAssembler接口
为简化配置甚至更多,Spring引入了AutodetectCapableMBeanInfoAssembler接口,它扩展了MbeanInfoAssembler接口以增加对自动检测Mbean资源的支持。如果你配置MbeanExporter有一个AutodetectCapableMBeanInfoAssembler实例,它允许"选取"bean的内容发布给JMX。
在该单元外,AutodetectCapableMBeanInfo接口的唯一实现是MetadataMBeanInfoAssembler,它将选择包含使用ManagedResource参数标示的任何bean,在这种情况中默认的方法是使用bean name作为Objectname,这将形成如下的配置:
注意在这个配置中并没有bean被传入到MbeanExporter中,然而在用ManagedResource 标示后JmxTestBean还是会被注册,MetadataMBeanInfoAssembler会检测并选取它。这种方法唯一的问题是JmxTestBean的名字现在有了业务上的意义,你可以通过修改创建ObjectName的默认动作来解决这个问题,正如19.4节 "Controlling the ObjectNames for your Beans"中定义的一样.
19.3.6. 使用Java接口定义管理接口
除MetadataMBeanInfoAssembler之外,Spring还包含了InterfaceBasedMBeanInfoAssembler,它允许你通过在接口中定义方法集的形式强制发布方法和属性。
尽管发布Mbean的标准机制是使用接口和一个简单的命名规则,InterfaceBasedMBeanInfoAssembler还是通过取消命名规则的需要扩展了这个功能,从而允许你使用多于一个的接口并且使你的bean不必实现Mbean接口。
设想用这个接口用来为前面看到的JmxTestBean类定义管理接口:
这个接口定义了将要在JMX Mbean中被发布为操作和参数的方法和属性。下面的代码显示了Spring JMX如何使用这个接口用作管理接口的定义:
这里你可以看到InterfaceBasedMBeanInfoAssembler被配置为使用IjmxTestBean接口去创建任何bean的管理接口。重要的是要明白通过InterfaceBasedMBeanInfoAssembler处理的beans没必要去实现产生JMX管理接口的接口。
在上面的例子中,IjmxTestBean接口被用来为所有接口构造所有的管理方法。在多数情况下,这并不是期望的动作,你可能想对不同的bean使用不同的接口,在这种情况下,你可能通过interfaceMappings属性传入一个properties给InterfaceBasedMBeanInfoAssembler,其中,每个条目的键值是bean的名字而每个条目的值是一个用于该bean的以逗号分隔的接口名清单。
如果没有通过managedInterfaces或interfaceMappings属性集指定管理接口,InterfaceBasedMBeanInfoAssembler将反射该bean并使用该bean实现的所有接口去创建管理接口。
19.3.7. 使用MethodNameBasedMBeanInfoAssembler
MethodNameBasedMBeanInfoAssembler允许你指定一个将要发布为JMX参数和操作的方法名列表,下面的代码展示了一个配置的例子:
这里你可以看到方法add和myOperation将被公布为JMX的操作,getName、setName和getAge作为适当的JMX参数。在上面的代码中,方法映射被应用到要发布到JMX的bean上。要控制基于bean by bean的方法发布,使用MethodNameMBeanInfoAssembler的methodMappings属性集来映射bean的名字到方法名列表。
19.4. 为你的bean控制ObjectName
在后台,MbeanExporter委派ObjectNamingStrategy的一个实现来获取每个已注册bean的ObjectName。默认的实现KeyNamingStrategy将使用beans Map的键值作为ObjectName。另外,KeyNamingStrategy可以映射beans Map的键值到属性文件的一个条目来分辨ObjectName。除KeyNamingStrategy之外,Spring提供两个额外的ObjectNamingStrategy实现:IdentityNamingStrategy能基于bean的标记符来构造ObjectName而MetadataNamingStrategy使用源码级元数据获取ObjectName。
19.4.1. 从属性文件读取ObjectName
你可以配置你自己的KeyNamingStrategy实例并设置它从一个Properties实例读取ObjectName而不是使用bean的键值。KeyNamingStrategy将试图在Properties中用与bean的键值对应的键来查找一个条目。如果没发现条目或Properties实例为空将使用bean自身的键值。
下面的代码显示了一个配置KeyNamingStrategy的例子:
这里的KeyNamingStrategy实例中配置了一个Properties实例,该实例结合了由map属性集定义的属性实例和属性文件路径。在这个配置中,testBean设定ObjectName为bean:name=testBean1,此后,它成为Properties实例的一个条目并有一个键值对应到bean的键值。
如果在Properties实例中没有发现该条目,bean的键值会被作为ObjectName.
19.4.2. 使用MetadataNamingStrategy
MetadataNamingStrategy使用每个bean上ManagedResource参数的objectName属性去创建ObjectName.下面的代码展示了MetadataNamingStrategy的配置:
19.5. 用JSR-160连接器发布你的bean
为进行远程访问,Spring JMX模块在org.springframework.jmx.support包中提供了两个FactoryBean的实现用于创建服务端和客户端的连接器。
19.5.1. 服务端连接器
Spring JMX使用下面的配置创建、启动和发布一个JSR-160 JMXConnectorServer:
通过默认的ConnectorServerFactoryBean创建一个JMXConnectorServer绑定到"service:jmx:jmxmp://localhost:9875"。ServerConnector从而在本机上通过JMXMP协议发布本机的MbeanServer给客户端。注意JMXMP协议在JSR160中标识为可选的:当前重要的开源JMX实现MX4J和J2SE5.0所提供的实现都未支持JMXMP。
要指定其它的URL并在MbeanServer中注册JMXConnectorServer本身,分别使用serviceUrl和objectName属性:
如果设置了ObjectName属性,Spring会自动随着ObjectName下的MbeanServer注册你的连接器。下面的例子显示了当创建JMXConnector时传递到ConnectorServerFactoryBean的完整参数集:
进一步的信息请参考JavaDoc。环境变量的说明信息,也请参考JavaDoc
注意当使用一个RMI-based连接器时,你需要启动lookup服务(tnameserv或rmiregistry)用来完成名称注册。如果你通过RMI使用Spring发布远程服务,Spring还要构造一个RMI注册项。如果没有,你可以容易地使用下面的配置项启动一个注册:
19.5.2. 客户端连接器
要创建一个到远程JSR-160激活的MbeanServer,使用如下的MbeanServerConnectionFactoryBean:
19.5.3. 通过Burlap/Hessian/SOAP的JMX
JSR-160允许扩展在客户端和服务端之间进行通讯的方法。上例中使用了JSR-160中RMI-based实现,JSR-160中要求通过(IIOP和JRMP)和可选的JMXMP的方式。使用其它的提供者或实现,如MX4J[http://mx4j.sourceforge.net],你可以使用基于HTTP或SSL方式的协议如SOAP、Hessian、Burlap和其它:
这个是使用MX4J 3.0.0的例子,更多信息请查阅MX4J的官方文档。
19.6. 通过代理访问MBeans
Spring JMX允许你创建代理改变路线调用在本地或远程MbeanServer上定义的Mbeans。该代理提供一个标准的Java接口使你能同你的Mbeans进行交互。下面的代码显示如何为运行在本地MbeanServer的MBean配置一个代理:
这里你可以看到为注册在ObjectName: bean:name=testBean下的Mbean创建了一个代理。该代理将实现的接口集由proxyInterfaces属性控制,而这个接口中方法和属性到Mbean上操作和参数的映射规则和在InterfaceBasedMBeanInfoAssembler中使用的一样。
MbeanProxyFactoryBean可以为任何能通过MbeanServerConnection访问的Mbean创建代理。默认的,本地MbeanServer只能在本地使用,但是你能覆盖它并提供一个MbeanServerConnection指向一个远程的MbeanServer,从而允许代理指向远程的Mbeans:
这里你可以看到我们使用MBeanServerConnectionFactoryBean创建一个MbeanServerConnection指向一个远程机器。该MbeanServerConnection通过server属性传入到MbeanProxyFactoryBean。所创建的代理通过这个MbeanServerConnection传递所有给MbeanServer的调用。
Spring的JMX支持所提供的特性使你容易而又透明地将你的Spring应用集成进一个JMX架构中。Spring JMX明确提供了4个核心特性:
- 自动注册任一个Spring bean为JMX Mbean
灵活的操纵你的bean管理接口的机制
通过远程,JSR-160连接器对外发布Mbeans
对本地和远程Mbean资源的简单代理
这些特性被设计用来使你的应用组件在不必耦合到Spring或JMX接口或类的情况下就可以工作。实际上,你的大部分应用类可以在不需要知道Spring或JMX的情况下使用Spring的JMX特性。
19.2. 导出你的Bean到JMX
Spring JMX框架中的核心类是MbeanExporter. 该类负责获取你的Spring beans并在JMX MbeanServer中注册它们。例如,假设有如下的简单bean类:
代码: |
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中的属性和方法公布为JMX Mbean中的参数和操作,你只要在配置文件 中简单的配置MbeanExporter类并传入如下的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> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans> |
这里,最重要的是定义exporter bean. beans属性被用来通知MbeanExporter你有那些beans将被发布到JMX MbeanServer. beans属性是Map类型,因而你能使用<map>和<entry>标记来配置要发布的beans. 在默认的配置中,Map中条目的键值被用作该条目中value所指bean的ObjectName. 这个动作可以按照19.4节"控制你的beans的ObjectName"的描述进行更改.
用这个配置,testBean在ObjectName bean:name=testBean1下被发布为JMX Mbean,。所有的公共属性被公布为参数而所有公共方法(除去在Object中定义的那些)被公布为操作。
19.2.1. 创建一个MBeanServer
如上所示的配置中假定该应用运行在一个有且只有一个MbeanServer正在运行的环境中。在这种情况下,Spring将定位正在运行的MbeanServer并注册你的beans到其中。这在你的应用运行在一个拥有自己的MbeanServer的容器内如Tomcat或者IBM WebSphere时很有用。
然而,这个方法无法用于独立环境中,或者当它运行在一个没有提供MbeanServer的容器时。为了克服这种困难,你可以通过在配置中声明添加一个org.springframework.jmx. support.MbeanServerFactoryBean的实例来创建MbeanServer的实例。通过设置MbeanExporter的server属性为MbeanServerFactoryBean,你同样可以确保这个MbeanServer被使用。如下所示:
代码: |
<beans> <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/> <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的实例被创建并通过service属性提供给MbeanExporter,当你提供你自己的MbeanServer时,MbeanExporter将不再尝试去访问正在运行的MbeanServer。为了让它正常工作,在你的classpath下必须有一个JMX的实现。
19.2.2. Mbean的延迟加载
如果你配置了一个bean为MbeanExporter,还可以配置它为延迟加载,这时MbeanExporter将不会违反这个约定并避免实例化这个bean. 作为代替,它将为MbeanServer注册一个代理并推迟从BeanFactory获取该bean直到该代理进行第一次调用。
19.2.3. 自动注册MBeans
任何通过MbeanExporter发布的beans和按照现在的样子在MbeanServer中定义的有效Mbeans并不需要Spring进一步介入。通过设置autodetect属性为true,Mbeans能被MBeanExporter自动探测到。
代码: |
<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注册。通常,自动探测到的bean用于JMX注册时用它们的bean名字作为ObjectName。这个动作可以按照18.4节"控制你的beans的ObjectName"的描述进行更改.
19.3. 控制你的bean的管理接口
在前面的例子中,你只能通过你的bean的管理接口对所有暴露出来的公共属性和方法进行很少的控制。为解决这个问题,Spring JMX提供一个全面又可扩展的机制来控制你的bean的管理接口。
19.3.1. MbeanInfoAssembler接口
在后台,MbeanExporter委派一个org.springframework.jmx.export. assembler.MbeanInfoAssembler接口的实现来负责为每个bean定义发布出来的管理接口。默认的实现org.springframework.jmx.export.assembler. SimpleReflectiveMBeanInfoAssembler,只是简单的定义一个接口发布所有的公共参数和方法,正如你在前面的例子中看到的一样。Spring为MbeanInfoAssembler接口提供两个额外的实现,允许你通过源码级元数据或任意的接口来控制管理接口。
19.3.2. 使用源码级元数据
利用MetadataMBeanInfoAssembler,你能使用源码级元数据为你的bean定义管理接口。元数据的读取被封装在org.springframework.jmx.export.metadata. JmxAttributeSource接口。在该单元外,Spring JMX为这个接口提供两种支持:支持普通参数的org.springframework.jmx.export.metadata. AttributesJmxAttributeSource和支持JDK5.0注解的org.springframework. jmx.export.annotation.AnnotationJmxAttributeSource。MetadataMBeanInfoAssembler必须要配置一个JmxAttributeSource的实现才能正确运行。在这个例子中,我们将使用普通参数元数据的方法。
要标示一个bean发布给JMX,你应当用ManagedResource参数来注解这个bean类。在使用普通参数元数据方法的场合中,这个类可以在org.springframework.jmx.metadata包中找到。每个要发布为操作的方法应当用ManagedOperation属性来标示,每个要发布的参数应当用ManagedAttribute参数来标示。当标示参数时,你可以省略getter或setter来分别创建一个只写或只读的参数。
下面的例子显示了用普通参数元数据标示前面看到的JmxTestBean类:
代码: |
package org.springframework.jmx; /** * @@org.springframework.jmx.export.metadata.ManagedResource * (description="My Managed Bean", objectName="spring:bean=test", * log=true, logFile="jmx.log", currencyTimeLimit=15, persistPolicy="OnUpdate", * persistPeriod=200, persistLocation="foo", persistName="bar") * */ public class JmxTestBean implements IJmxTestBean { private String name; private int age; /** * @@org.springframework.jmx.export.metadata.ManagedAttribute * (description="The Age Attribute", currencyTimeLimit=15) */ public int getAge() { return age; } public void setAge(int age) { this.age = age; } /** * @@org.springframework.jmx.export.metadata.ManagedAttribute * (description="The Name Attribute", currencyTimeLimit=20, * defaultValue="bar", persistPolicy="OnUpdate") */ public void setName(String name) { this.name = name; } /** * @@org.springframework.jmx.export.metadata.ManagedAttribute * (defaultValue="foo", persistPeriod=300) */ public String getName() { return name; } /** * @@org.springframework.jmx.export.metadata.ManagedOperation * (description="Add Two Numbers Together") */ public int add(int x, int y) { return x + y; } public void dontExposeMe() { throw new RuntimeException(); } } |
这里你可以看到,JmxTestBean类被ManagedResource参数标示并且ManagedResource参数被配置到一个属性集。这些属性能被用于配置由MbeanExporter产生的Mbean的不同的切面,进一步的说明请参见19.3.4 "Source-Level Metadata Types".
你同样注意到age和name属性都用ManagedAttribute来标示,但是age属性只标示了getter方法。这将使得这两个属性都作为参数被包含在管理接口中,并且age参数是只读的。
最后,你会注意到add(int, int)方法用ManagedOperation参数来标示而dontExposeMe()方法没有标示。这将使得当使用MetadataMBeanInfoAssembler时,管理接口中只包含一个操作add(int, int)。
下面的代码展示了如何使用MetadataMBeanInfoAssembler来配置MbeanExporter:
代码: |
<beans> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="bean:name=testBean1"> <ref local="testBean"/> </entry> </map> </property> <property name="assembler"> <ref local="assembler"/> </property> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name"> <value>TEST</value> </property> <property name="age"> <value>100</value> </property> </bean> <bean id="attributeSource" class="org.springframework.jmx.export.metadata.AttributesJmxAttributeSource"> <property name="attributes"> <bean class="org.springframework.metadata.commons.CommonsAttributes"/> </property> </bean> <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"> <property name="attributeSource"> <ref local="attributeSource"/> </property> </bean> </beans> |
这里你可以看到,一个MetadataMBeanInfoAssembler bean设置了一个AttributesJmxAttributeSource的实例,并且通过assembler属性传递到MbeanExporter中,这就是在你的Spring-exposed Mbeans中利用metadata-driven管理接口时所有的要求。
19.3.3. 使用JDK 5.0的注解
要在管理接口定义中使用JDK5.0的注解,Spring提供了一个注解集镜像到Commons Attribute参数类和一个JMX的实现来支持JmxAttributeSource, AnnotationsJmxAttributeSource并允许MbeanInfoAssembler读取它们。
下面的例子显示了一个用JDK5.0注解定义管理接口的bean:
代码: |
package org.springframework.jmx; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedAttribute; @ManagedResource(objectName="bean:name=testBean4", description="My Managed Bean", log=true, logFile="jmx.log", currencyTimeLimit=15, persistPolicy="OnUpdate", persistPeriod=200, persistLocation="foo", persistName="bar") public class AnnotationTestBean implements IJmxTestBean { private String name; private int age; @ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15) public int getAge() { return age; } public void setAge(int age) { this.age = age; } @ManagedAttribute(description="The Name Attribute", currencyTimeLimit=20, defaultValue="bar", persistPolicy="OnUpdate") public void setName(String name) { this.name = name; } @ManagedAttribute(defaultValue="foo", persistPeriod=300) public String getName() { return name; } @ManagedOperation(description="Add Two Numbers Together") public int add(int x, int y) { return x + y; } public void dontExposeMe() { throw new RuntimeException(); } } |
正如你所见,除了元数据定义的语法外只有很少的变动。这个方法在后台启动时会有一点缓慢,因为要在类中使用Commons Attributes转换JDK5.0的注解。不过,这仅仅是一次性的开销并且JDK5.0的注解为你带来编译时检查的好处。
19.3.4. Source-Level Metadata Types
下面是Spring JMX中使用的源码级元数据类型:
- Purpose Commons AttributesAttribute JDK 5.0 Annotation Attribute / AnnotationType
Mark all instances of a Class as JMX managed resources ManagedResource @ManagedResource Class
Mark a method as a JMX operation ManagedOperation @ManagedOperation Method
Mark a getter or setter as one half of a JMX attribute ManagedAttribute @ManagedAttribute Method (only getters and setters)
Define descriptions for operation parameters ManagedOperationParameters @rManagedOperationParameter and@ManagedOperationParameters Method
Table 19.1. Source-Level Metadata Types
下面是源码级元数据类型中使用的配置参数:
- Parameter Description Applies to
objectName Used by MetadataNamingStrategy to determine the ObjectName of a managed resource managed resource
description Sets the friendly description of the resource, attribute or operation ManagedResource,ManagedAttribute,ManagedOperation,ManagedOperationParameter
currencyTimeLimit Sets the value of the currencyTimeLimit descriptor field ManagedResource,ManagedAttribute
defaultValue Sets the value of the defaultValue descriptor field ManagedAttribute
log Sets the value of the log descriptor field ManagedResource
logFile Sets the value of the logFile descriptor field ManagedResource
persistPolicy Sets the value of the persistPolicy descriptor field ManagedResource
persistPeriod Sets the value of the persistPeriod descriptor field ManagedResource
persistLocation Sets the value of the persistLocation descriptor field ManagedResource
persistName Sets the value of the persistName descriptor field ManagedResource
name Sets the display name of an operation parameter ManagedOperationParameter
index Sets the index of an operation parameter ManagedOperationParameter
Table 19.2. Source-Level Metadata Parameters
19.3.5. AutodetectCapableMBeanInfoAssembler接口
为简化配置甚至更多,Spring引入了AutodetectCapableMBeanInfoAssembler接口,它扩展了MbeanInfoAssembler接口以增加对自动检测Mbean资源的支持。如果你配置MbeanExporter有一个AutodetectCapableMBeanInfoAssembler实例,它允许"选取"bean的内容发布给JMX。
在该单元外,AutodetectCapableMBeanInfo接口的唯一实现是MetadataMBeanInfoAssembler,它将选择包含使用ManagedResource参数标示的任何bean,在这种情况中默认的方法是使用bean name作为Objectname,这将形成如下的配置:
代码: |
<beans> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="assembler" ref="assembler"/> <property name="autodetect" value="true"/> </bean> <bean id="bean:name=testBean1" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> <bean id="attributeSource" class="org.springframework.jmx.export.metadata.AttributesJmxAttributeSource"/> <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"> <property name="attributeSource" ref="attributeSource"/> </bean> </beans> |
注意在这个配置中并没有bean被传入到MbeanExporter中,然而在用ManagedResource 标示后JmxTestBean还是会被注册,MetadataMBeanInfoAssembler会检测并选取它。这种方法唯一的问题是JmxTestBean的名字现在有了业务上的意义,你可以通过修改创建ObjectName的默认动作来解决这个问题,正如19.4节 "Controlling the ObjectNames for your Beans"中定义的一样.
19.3.6. 使用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"> <ref local="testBean"/> </entry> </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</value> </property> <property name="age"> <value>100</value> </property> </bean> </beans> |
这里你可以看到InterfaceBasedMBeanInfoAssembler被配置为使用IjmxTestBean接口去创建任何bean的管理接口。重要的是要明白通过InterfaceBasedMBeanInfoAssembler处理的beans没必要去实现产生JMX管理接口的接口。
在上面的例子中,IjmxTestBean接口被用来为所有接口构造所有的管理方法。在多数情况下,这并不是期望的动作,你可能想对不同的bean使用不同的接口,在这种情况下,你可能通过interfaceMappings属性传入一个properties给InterfaceBasedMBeanInfoAssembler,其中,每个条目的键值是bean的名字而每个条目的值是一个用于该bean的以逗号分隔的接口名清单。
如果没有通过managedInterfaces或interfaceMappings属性集指定管理接口,InterfaceBasedMBeanInfoAssembler将反射该bean并使用该bean实现的所有接口去创建管理接口。
19.3.7. 使用MethodNameBasedMBeanInfoAssembler
MethodNameBasedMBeanInfoAssembler允许你指定一个将要发布为JMX参数和操作的方法名列表,下面的代码展示了一个配置的例子:
代码: |
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="bean:name=testBean5"> <ref local="testBean"/> </entry> </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和getAge作为适当的JMX参数。在上面的代码中,方法映射被应用到要发布到JMX的bean上。要控制基于bean by bean的方法发布,使用MethodNameMBeanInfoAssembler的methodMappings属性集来映射bean的名字到方法名列表。
19.4. 为你的bean控制ObjectName
在后台,MbeanExporter委派ObjectNamingStrategy的一个实现来获取每个已注册bean的ObjectName。默认的实现KeyNamingStrategy将使用beans Map的键值作为ObjectName。另外,KeyNamingStrategy可以映射beans Map的键值到属性文件的一个条目来分辨ObjectName。除KeyNamingStrategy之外,Spring提供两个额外的ObjectNamingStrategy实现:IdentityNamingStrategy能基于bean的标记符来构造ObjectName而MetadataNamingStrategy使用源码级元数据获取ObjectName。
19.4.1. 从属性文件读取ObjectName
你可以配置你自己的KeyNamingStrategy实例并设置它从一个Properties实例读取ObjectName而不是使用bean的键值。KeyNamingStrategy将试图在Properties中用与bean的键值对应的键来查找一个条目。如果没发现条目或Properties实例为空将使用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实例,该实例结合了由map属性集定义的属性实例和属性文件路径。在这个配置中,testBean设定ObjectName为bean:name=testBean1,此后,它成为Properties实例的一个条目并有一个键值对应到bean的键值。
如果在Properties实例中没有发现该条目,bean的键值会被作为ObjectName.
19.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.metadata.AttributesJmxAttributeSource"/> </beans> |
19.5. 用JSR-160连接器发布你的bean
为进行远程访问,Spring JMX模块在org.springframework.jmx.support包中提供了两个FactoryBean的实现用于创建服务端和客户端的连接器。
19.5.1. 服务端连接器
Spring JMX使用下面的配置创建、启动和发布一个JSR-160 JMXConnectorServer:
代码: |
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"/> |
通过默认的ConnectorServerFactoryBean创建一个JMXConnectorServer绑定到"service:jmx:jmxmp://localhost:9875"。ServerConnector从而在本机上通过JMXMP协议发布本机的MbeanServer给客户端。注意JMXMP协议在JSR160中标识为可选的:当前重要的开源JMX实现MX4J和J2SE5.0所提供的实现都未支持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> |
进一步的信息请参考JavaDoc。环境变量的说明信息,也请参考JavaDoc
注意当使用一个RMI-based连接器时,你需要启动lookup服务(tnameserv或rmiregistry)用来完成名称注册。如果你通过RMI使用Spring发布远程服务,Spring还要构造一个RMI注册项。如果没有,你可以容易地使用下面的配置项启动一个注册:
代码: |
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"> <property name="port" value="1099"/></bean> |
19.5.2. 客户端连接器
要创建一个到远程JSR-160激活的MbeanServer,使用如下的MbeanServerConnectionFactoryBean:
代码: |
<bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean"> <property name="serviceUrl" value="service:jmx:rmi://localhost:9875"/> </bean> |
19.5.3. 通过Burlap/Hessian/SOAP的JMX
JSR-160允许扩展在客户端和服务端之间进行通讯的方法。上例中使用了JSR-160中RMI-based实现,JSR-160中要求通过(IIOP和JRMP)和可选的JMXMP的方式。使用其它的提供者或实现,如MX4J[http://mx4j.sourceforge.net],你可以使用基于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的官方文档。
19.6. 通过代理访问MBeans
Spring JMX允许你创建代理改变路线调用在本地或远程MbeanServer上定义的Mbeans。该代理提供一个标准的Java接口使你能同你的Mbeans进行交互。下面的代码显示如何为运行在本地MbeanServer的MBean配置一个代理:
代码: |
<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean"> <property name="objectName"> <value>bean:name=testBean</value> </property> <property name="proxyInterface"> <value>org.springframework.jmx.IJmxTestBean</value> </property> </bean> |
这里你可以看到为注册在ObjectName: bean:name=testBean下的Mbean创建了一个代理。该代理将实现的接口集由proxyInterfaces属性控制,而这个接口中方法和属性到Mbean上操作和参数的映射规则和在InterfaceBasedMBeanInfoAssembler中使用的一样。
MbeanProxyFactoryBean可以为任何能通过MbeanServerConnection访问的Mbean创建代理。默认的,本地MbeanServer只能在本地使用,但是你能覆盖它并提供一个MbeanServerConnection指向一个远程的MbeanServer,从而允许代理指向远程的Mbeans:
代码: |
<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"/> </bean> |
这里你可以看到我们使用MBeanServerConnectionFactoryBean创建一个MbeanServerConnection指向一个远程机器。该MbeanServerConnection通过server属性传入到MbeanProxyFactoryBean。所创建的代理通过这个MbeanServerConnection传递所有给MbeanServer的调用。