©
This document uses PHP Chinese website manual Release
Spring提供的JMX对JMX通知包含了全面的支持。
Spring的JMX支持使得用任意数量MBean注册任意数量的 NotificationListeners
监听器(包括由Spring的 MBeanExporter
输出和其他机制注册的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
都能得到通知。
ConsoleLoggingNotificationListener
就可以采取任何它认为合适的行为来响应 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
列表属性来替代(优先于 notificationListenerMappings
属性)。
这时就要配置多个 NotificationListenerBean
实例,而不仅仅是一个了……
一个 NotificationListenerBean
不但封装了一个或者多个 NotificationListener
和已注册到一个 MBeanServer
的 ObjectName
,它也封装了许多其他属性,例如一个 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
发生时,我们想得到一个回传对象,
且想通过提供一个 NotificationFilter
过滤出无关的 Notifications
。
至于什么是一个回传对象,NotificationFilter
到底又是什么的全面的探讨,请参考JMX规范(1.2)'The JMX Notification Model'
章节。
<beans> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="bean:name=testBean1" value-ref="testBean1"/> <entry key="bean:name=testBean2" value-ref="testBean2"/> </map> </property> <property name="notificationListeners"> <list> <bean class="org.springframework.jmx.export.NotificationListenerBean"> <constructor-arg ref="customerNotificationListener"/> <property name="mappedObjectNames"> <list> <!-- handles notifications from two distinct MBeans --> <value>bean:name=testBean1</value> <value>bean:name=testBean2</value> </list> </property> <property name="handback"> <bean class="java.lang.String"> <constructor-arg value="This could be anything..."/> </bean> </property> <property name="notificationFilter" ref="customerNotificationListener"/> </bean> </list> </property> </bean> <!-- 实现了NotificationListener
和NotificationFilter
接口 --> <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>
Spring不但提供了注册接收通知的支持,也提供了对发布通知的支持。
要注意的是,本章节仅仅与通过 MBeanExporter
暴露的,被Spring管理的Bean相关。
任何现存的,用户定义的MBean应当使用标准JMX API来做通知发布。
Spring的JMX通知发布支持中的关键接口是 NotificationPublisher
(定义于 org.springframework.jmx.export.notification
包中)。
任意要通过 MBeanExporter
实例输出为MBean的Bean都可以实现
NotificationPublisherAware
接口来获得对 NotificationPublisher
实例的访问。
NotificationPublisherAware
仅仅提供通过一个简单的setter方法给实现了这个接口的Bean注入一个
NotificationPublisher
实例,那些Bean就因此可以发布 Notification
了。
就如 NotificationPublisher
类的Javadoc描述的一样,通过 NotificationPublisher
机制发布事件的受控Bean是 不 需要对任何通知的监听器或者其他诸如此类的监听器的状态管理负责的。Spring的JMX支持将处理与JMX架构相关的所有问题。
作为一个应用程序开发者,他所需要做的只是实现 NotificationPublisherAware
接口,然后利用注入的 NotificationPublisher
实例发布事件。要注意,受控Bean注册到一个 MBeanServer
后,NotificationPublisher
才被设置。
使用 NotificationPublisher
实例的方法很直观,人们只要构建一个 Notification
实例(或者一个合适的 Notification
子类的实例),接着填充与将要发布的事件相关的数据到通知里,然后传入 Notification
,调用NotificationPublisher
实例的方法 sendNotification(Notification)
就可以了。
让我们来看一个简单的例子,在这个场景里,输出 JmxTestBean
实例在每次 add(int, int)
操作调用时都会发布 NotificationEvent
。
package org.springframework.jmx;
import org.springframework.jmx.export.notification.NotificationPublisherAware;
import org.springframework.jmx.export.notification.NotificationPublisher;
import javax.management.Notification;
public class JmxTestBean implements IJmxTestBean, NotificationPublisherAware {
private String name;
private int age;
private boolean isSuperman;
private NotificationPublisher publisher;
// 清晰起见,忽略了其他getter和setter
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的紧耦合,那么就行动吧。