21.7.通知
21.7. 通知
Spring的JMX提供的内容包括了对JMX通知的全面的支持。
21.7.1. 为通知注册监听器
Spring的JMX支持使得用任意数量的MBean(这包括由Spring的 MBeanExporter
输出的MBean和通过其他机制注册的MBean)注册任意数量的 NotificationListeners
非常容易。 例子最能阐明影响 NotificationListeners
的注册有多么简单。 考虑一个场景,任何时候一个目标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"> <property name="beans"> <map> <entry key="bean:name=testBean1" value-ref="testBean"/> </map> </property> <property name="notificationListenerMappings"> <map> <entry key="bean:name=testBean1"> <bean/> </entry> </map> </property> </bean> <bean id="testBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans>
有了上面的配置,每次来自目标MBean(bean:name=testBean1
)的一个JMX的 Notification
都会被广播, 通过属性 notificationListenerMappings
注册的作为监听器的 ConsoleLoggingNotificationListener
bean将得到通知。 然后 ConsoleLoggingNotificationListener
bean可以采取任何它认为合适的行动来响应这个 Notification
。
如果想给所有的正在输出的已经装入MBeanExporter
的bean注册单个 NotificationListener
实例,可以用特殊的通配符'*'
(没有引号) 作为属性映射notificationListenerMappings
的一个实体的键,看这段代码……
<property name="notificationListenerMappings"> <map> <entry key="*"> <bean/> </entry> </map> </property>
如果想与上面相反(即,为一个MBean注册多个不同的监听器),那么就必须使用 notificationListeners
这个列表属性来代替(优先于属性notificationListenerMappings
)。 这次,要配置多个 NotificationListenerBean
实例,而不是简单的为单个MBean配置一个 NotificationListener
……一个 NotificationListenerBean
封装了一个 NotificationListener
和 ObjectName
(或ObjectNames
) 这样,它就在一个 MBeanServer
里进行注册。 NotificationListenerBean
也封装了许多其他的属性, 如NotificationFilter
,可以用于高级JMX通知场景的任意用于的回传对象等。
使用 NotificationListenerBean
实例的时的配置跟之前的配置并没有很大的不同:
<beans> <bean id="exporter"> <property name="beans"> <map> <entry key="bean:name=testBean1" value-ref="testBean"/> </map> </property> <property name="notificationListeners"> <list> <bean> <constructor-arg> <bean/> </constructor-arg> <property name="mappedObjectNames"> <list> <bean> <constructor-arg value="bean:name=testBean1"/> </bean> </list> </property> </bean> </list> </property> </bean> <bean id="testBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans>
上面的例子跟第一个通知的例子差不多。假设每次产生一个 Notification
我们都想得到一个回传对象(handback object), 此外,我们想通过提供一个 NotificationFilter
来过滤掉无关的Notifications
。 (要全面的讨论什么是一个回传对象,NotificationFilter
实际上是什么,请参考JMX规范(1.2)相应的章节 'The JMX Notification Model'
。)
<beans> <bean id="exporter"> <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> <constructor-arg ref="customerNotificationListener"/> <property name="mappedObjectNames"> <list> <!-- let's handle notifications from two distinct MBeans --> <bean> <constructor-arg value="bean:name=testBean1"/> </bean> <bean> <constructor-arg value="bean:name=testBean2"/> </bean> </list> </property> <property name="handback"> <bean> <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"/> <bean id="testBean1"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> <bean id="testBean2"> <property name="name" value="ANOTHER TEST"/> <property name="age" value="200"/> </bean> </beans>
21.7.2. 发布通知
Spring不只提供接受 Notifications
的注册,还提供发布 Notifications
。
注意
请注意这一章只是与Spring的那些通过 MBeanExporter
所谓MBean输出的管理bean有关系;任何现存的,用户定义的MBean必须使用标准的JMX API来发布通知。
在Spring的JMX通知发布支持中,关键的是 NotificationPublisher
接口 (在包 org.springframework.jmx.export.notification
中定义的)。 任一将要通过 MBeanExporter
作为MBean输出的bean实例都可以实现对应的 NotificationPublisherAware
接口来获取对接口 NotificationPublisher
的访问。 NotificationPublisherAware
接口只提供了一个 NotificationPublisher
的实例通过简单的setter方法实现bean,这样bean就可以用于发布 Notifications
。
如同在Javadoc中对类 NotificationPublisher
的描述,通过 NotificationPublisher
机制发布事件的管理bean 不 负责 任何通知监听器以及诸如此类……的状态管理。Spring的JMX支持谨慎的处理所有的JMX架构问题。应用程序开发者所需要的 就是实现接口 NotificationPublisherAware
,用提供的接口 NotificationPublisher
开始发布事件。 注意,在已经用一个 MBeanServer
将管理bean注册之后, 再设置NotificationPublisher
。
用 NotificationPublisher
的实例非常简单的…… 仅仅创建一个 Notification
实例 (或一个适当的 Notification
子类的实例),带有与事件相关联数据的通知将被发布, 然后在 NotificationPublisher
实例上调用sendNotification(Notification)
, 在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; } }
Spring的JMX提供的接口 NotificationPublisher
和让其工作的辅助装置是一个非常好的特性。 的确,伴随而来的是你的类与Spring和JMX的耦合开销,跟平时一样,这里的建议也是比较实际的, 如果你需要 NotificationPublisher
提供的功能,并且你可以接受与Spring和JMX的耦合,那么就去做。