1、可以自動探測實現MBean接口的MBean對象,而且可以將一個普通的Spring Bean註冊爲MBean;
2、定製管理MBean的接口,根據需要暴露特定管理MBean的操作;
3、使用註解定義MBean管理接口;
4、可以實現對本地和遠程MBean的代理。
標準MBean管理
對於實現標準MBean接口MBean資源,在Spring中可以設置不同的探測模式,主要是通過MBeanExporter來實現。例如,定義MBean管理接口:
- package org.shirdrn.spring.jmx.autodetect;
- public interface MyObjectMBean {
- public long getId();
- public void setId(long id);
- public String getName();
- public void setName(String name);
- public String show();
- }
對應的MBean實現,如下所示:
- package org.shirdrn.spring.jmx.autodetect;
- import org.apache.log4j.Logger;
- public class MyObject implements MyObjectMBean {
- private static final Logger LOG = Logger.getLogger(MyObject.class);
- private long id;
- private String name;
- public MyObject() {
- super();
- }
- public MyObject(long id, String name) {
- super();
- this.id = id;
- this.name = name;
- }
- public long getId() {
- return id;
- }
- public void setId(long id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String show() {
- StringBuffer sb = new StringBuffer().append("id=").append(id).append(
- ", name=").append(name);
- LOG.info("show()=" + sb.toString());
- return sb.toString();
- }
- }
上面是一個標準MBean的實現,可以使用JavaSE 6平臺的JMX服務來管理,但是在Spring集成的環境下,也可以重用這些MBean實現,例如下面是一個典型的配置:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans
- xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- <bean id="mbServer" class="org.springframework.jmx.export.MBeanExporter">
- <property name="autodetectModeName">
- <value>AUTODETECT_ALL</value>
- </property>
- </bean>
- <bean name="mydomain:myobj=MyObjectMBean" class="org.shirdrn.spring.jmx.autodetect.MyObject">
- <property name="id">
- <value>90000000001</value>
- </property>
- <property name="name">
- <value>shirdrn</value>
- </property>
- </bean>
- </beans>
上面配置中,MBeanExporter會查找本地MBean Server,指定的探測模式autodetectModeName爲AUTODETECT_ALL,這也是MBeanExporter的默認值(這個屬性完全可以省略,不用配置),無需手動向MBean Server進行註冊,便能管理配置的MBean對象“mydomain:myobj=MyObjectMBean”。
對於探測模式autodetectModeName屬性,Spring提供了4個取值:
AUTODETECT_NONE 不啓用自動探測,需要手動向MBean Server進行註冊,即通過MBeanExporter的beans屬性進入註冊;
AUTODETECT_MBEAN 在當前IOC容器中進行查找MBean組件;
AUTODETECT_ASSEMBLER 設置根據MBeanInfoAssembler的策略進行探測;
AUTODETECT_ALL 自動探測,是AUTODETECT_MBEAN和AUTODETECT_ASSEMBLER的並集。
另外,Spring的MBeanExporter也提供了autodetect屬性,取值爲true和false,指定對MBean組件的探測行爲。
普通Spring Bean管理
對於一個普通的Spring Bean,也可以作爲MBean來進行管理,Spring可以很好地支持。如下面一個普通的Java類:
- package org.shirdrn.spring.jmx;
- import org.apache.log4j.Logger;
- public class MyJavaObject {
- private static final Logger LOG = Logger.getLogger(MyJavaObject.class);
- private long id;
- private String name;
- public MyJavaObject() {
- super();
- }
- public MyJavaObject(long id, String name) {
- super();
- this.id = id;
- this.name = name;
- }
- public long getId() {
- return id;
- }
- public void setId(long id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String show() {
- StringBuffer sb = new StringBuffer().append("id=").append(id).append(
- ", name=").append(name);
- LOG.info("show()=" + sb.toString());
- return sb.toString();
- }
- }
它並沒有實現MBean管理接口,可以通過MBeanExporter的beans屬性進行註冊,配置如下所示:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans
- xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- <bean id="mbServer" class="org.springframework.jmx.export.MBeanExporter">
- <property name="beans">
- <map>
- <entry key="mydomain:myjavaobj=MyJavaObject" value-ref="myobj"/>
- </map>
- </property>
- </bean>
- <bean name="myobj" class="org.shirdrn.spring.jmx.MyJavaObject">
- <property name="id">
- <value>90000000001</value>
- </property>
- <property name="name">
- <value>shirdrn</value>
- </property>
- </bean>
- </beans>
因爲org.shirdrn.spring.jmx.MyJavaObject沒有對應的MBean接口,所以默認情況下,該類中public的成員都會暴露出來,通過MBean Server可以管理。實際上,系統中MBean的某些屬性或方法可能不需要暴露給外部進行管理,爲了克服這種缺點,Spring提供了基於方法列表和接口定製的功能,可以將你所感興趣的屬性或方法暴露給外部管理。
基於方法列表和接口定製的MBean管理
對於上述普通的Java類MyJavaObject,可以通過定製接口和方法列表,來暴露MBean屬性或方法。例如,一個方法列表,可以在進行配置的時候指定,如下所示:
- <bean id="assembler" class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler">
- <property name="managedMethods" value="setId,setName,show" />
- </bean>
上面通過org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler來進行方法列表的設置。而對於接口,可以定義一個接口,如下所示:
- package org.shirdrn.spring.jmx.selected.interfaces;
- public interface SelectedMethodsInterface {
- public long setId(long id);
- public void setName(String name);
- public void show();
- }
Spring配置,如下所示:
- <bean id="assembler" class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
- <property name="managedInterfaces">
- <list>
- <value>org.shirdrn.spring.jmx.selected.interfaces.SelectedMethodsInterface</value>
- </list>
- </property>
- </bean>
上面通過org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler,可以設置一個接口列表,來管理MBean暴露的接口。
對於上面兩種方式,都得到一個assembler實例,需要將其注入到org.springframework.jmx.export.MBeanExporter中,如下所示:
- <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
- <property name="beans">
- <map>
- <entry key="mydomain:javaObj=MyJavaObject" value-ref="javaObject" />
- </map>
- </property>
- <property name="assembler" ref="assembler" />
- </bean>
基於註解的MBean管理
對於一個普通的Java類,作爲MBean需要被管理,可以通過註解指定要暴露的屬性和方法,示例如下:
- package org.shirdrn.spring.jmx.annotation;
- import org.apache.log4j.Logger;
- import org.springframework.jmx.export.annotation.ManagedAttribute;
- import org.springframework.jmx.export.annotation.ManagedOperation;
- import org.springframework.jmx.export.annotation.ManagedResource;
- @ManagedResource(objectName = "annojmx:myjao=AnnotationObject", description = "MyJavaAnnotationObject")
- public class MyJavaAnnotationObject {
- private static final Logger LOG = Logger.getLogger(MyJavaAnnotationObject.class);
- private long id;
- private String name;
- public MyJavaAnnotationObject() {
- super();
- }
- public MyJavaAnnotationObject(long id, String name) {
- super();
- this.id = id;
- this.name = name;
- }
- @ManagedAttribute
- public long getId() {
- return id;
- }
- public void setId(long id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- @ManagedAttribute
- public void setName(String name) {
- this.name = name;
- }
- @ManagedOperation
- public String show() {
- StringBuffer sb = new StringBuffer().append("id=").append(id).append(
- ", name=").append(name);
- LOG.info("show()=" + sb.toString());
- return sb.toString();
- }
- }
上面@ManagedResource表示指定該類的實例作爲MBean註冊到MBean Server中,然後可以通過對屬性和方法分別使用@ManagedAttribute和@ManagedOperation來指定暴露的屬性和方法。有關這些註解的詳細內容,可以查閱相關文檔。
下面是一個基本的配置內容:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
- <property name="assembler" ref="assembler" />
- <property name="namingStrategy" ref="namingStrategy" />
- </bean>
- <bean id="assembler"
- class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
- <property name="attributeSource" ref="jmxas" />
- </bean>
- <bean id="namingStrategy"
- class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
- <property name="attributeSource" ref="jmxas" />
- </bean>
- <bean id="jmxas" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" />
- <bean id="javaObj"
- class="org.shirdrn.spring.jmx.annotation.MyJavaAnnotationObject">
- <property name="id" value="201122121200" />
- <property name="name" value="shirdrn" />
- </bean>
- </beans>
上面使用了Spring的MBeanExporter,可以看到,Spring配置內容相對較多。
Spring還提供兩種簡化配置的方式,
一種是,提供了org.springframework.jmx.export.annotation.AnnotationMBeanExporter,可以將上述配置大大簡化,等價的配置如下所示:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- <bean class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter"/>
- <bean name="myAnnoObject"
- class="org.shirdrn.spring.jmx.annotation.MyJavaAnnotationObject">
- <property name="id" value="201122121200" />
- <property name="name" value="shirdrn" />
- </bean>
- </beans>
另一種是,提供了<context:mbean-export />標籤,更加簡潔,等價的配置如下所示:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-2.5.xsd">
- <context:mbean-export registration="failOnExisting" />
- <bean name="myAnnoObject"
- class="org.shirdrn.spring.jmx.annotation.MyJavaAnnotationObject">
- <property name="id" value="201122121200" />
- <property name="name" value="shirdrn" />
- </bean>
- </beans>
通過遠程代理訪問MBean
根據JavaSE 6平臺的JMX技術架構定義,分爲設備層、代理層、遠程管理層這三層,我們通過可以使用Spring提供的org.springframework.jmx.access.MBeanProxyFactoryBean實現,定義代理,來訪問MBean Server管理MBean資源。實際上,Spring提供的這個代理功能位於JMX架構的遠程管理層,那麼在代理層和遠程管理層之間,要定義連接器,才能通過遠程管理層訪問到代理層的MBean Server組件。下面通過實例來實現:
首先,定義個普通的Java類,作爲待管理的MBean,如下所示:
- package org.shirdrn.spring.jmx.rmi;
- import org.apache.log4j.Logger;
- public class MyJavaObject {
- private static final Logger LOG = Logger.getLogger(MyJavaObject.class);
- private long id;
- private String name;
- public MyJavaObject() {
- super();
- }
- public MyJavaObject(long id, String name) {
- super();
- this.id = id;
- this.name = name;
- }
- public long getId() {
- return id;
- }
- public void setId(long id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String show() {
- StringBuffer sb = new StringBuffer().append("id=").append(id).append(
- ", name=").append(name);
- LOG.info("show()=" + sb.toString());
- return sb.toString();
- }
- }
其次,定義了一個Java接口,來按需暴露操作MBean資源的方法:
- package org.shirdrn.spring.jmx.rmi;
- public interface MyManagedInterfaces {
- public long setId(long id);
- public String getName();
- public String show();
- }
再次,看一下我們模擬代理層Spring配置server.xml內容:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
- <property name="beans">
- <map>
- <entry key="mydomain:myjavaobj=MyJavaObject" value-ref="myJavaObj" />
- </map>
- </property>
- <property name="assembler" ref="assembler" />
- </bean>
- <bean id="assembler"
- class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
- <property name="managedInterfaces">
- <list>
- <value>org.shirdrn.spring.jmx.rmi.MyManagedInterfaces</value>
- </list>
- </property>
- </bean>
- <bean name="myJavaObj" class="org.shirdrn.spring.jmx.rmi.MyJavaObject">
- <property name="id" value="88000000001" />
- <property name="name" value="shirdrn" />
- </bean>
- <bean id="server" class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry">
- <property name="objectName">
- <value>connector:name=rmi</value>
- </property>
- <property name="serviceUrl">
- <value>service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi</value>
- </property>
- </bean>
- <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
- <property name="port">
- <value>1099</value>
- </property>
- </bean>
- </beans>
上半部分配置已經非常熟悉,就是將MBean註冊到MBean Server中。下半部分是有關連接器的配置,它是基於RMI協議來進行適配,通過Spring提供的org.springframework.jmx.support.ConnectorServerFactoryBean來實現器,這樣,遠程管理層可以連接到啓動的連接服務器,來訪問MBean資源。
然後,我們啓動MBean Server及其連接服務器,代碼如下所示:
- package org.shirdrn.spring.jmx.rmi;
- import org.apache.log4j.Logger;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class Server {
- private static final Logger LOG = Logger.getLogger(Server.class);
- public static void main(String[] args) throws Exception {
- new ClassPathXmlApplicationContext("org/shirdrn/spring/jmx/rmi/server.xml");
- LOG.info("Server started.");
- Object lock = new Object();
- synchronized (lock) {
- lock.wait();
- }
- }
- }
啓動後,我們定義的MBean已經註冊到本地MBean Server中,同時啓動了連接器,監聽1099端口。
接着,我們定義遠程訪問層的Spring配置proxy.xml內容,如下所示:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- <bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
- <property name="connectOnStartup" value="true" />
- <property name="objectName" value="mydomain:myjavaobj=MyJavaObject" />
- <property name="proxyInterface">
- <value>org.shirdrn.spring.jmx.rmi.MyManagedInterfaces</value>
- </property>
- <property name="serviceUrl">
- <value>service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi</value>
- </property>
- </bean>
- </beans>
最後,啓動代理,訪問MBean資源,代碼如下所示:
- package org.shirdrn.spring.jmx.rmi;
- import org.apache.log4j.Logger;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- public class Proxy {
- private static final Logger LOG = Logger.getLogger(Proxy.class);
- public static void main(String[] args) throws Exception {
- ApplicationContext ctx = new ClassPathXmlApplicationContext(
- "org/shirdrn/spring/jmx/rmi/proxy.xml");
- LOG.info("Proxy server started.");
- MyManagedInterfaces proxy = (MyManagedInterfaces) ctx.getBean("proxy");
- String message = proxy.show();
- LOG.info("proxy.show() = " + message);
- Object lock = new Object();
- synchronized (lock) {
- lock.wait();
- }
- }
- }
這裏,只能訪問通過org.shirdrn.spring.jmx.rmi.MyManagedInterfaces接口定義的方法來操作註冊到MBean Server中的MBean資源。