通过查询MBean获得信息只是查看应用状态的一种方法。但当应用发生重要事件时,如果希望 能够及时告知我们,这通常不是最有效的方法。
例如,假设Spittr应用保存了已发布的Spittle数量,而我们希望知道每发布一百万Spittle时的精 确时间(例如一百万、两百万、三百万等)。一种解决方法是编写代码定期查询数据库,计算Spittle的数量。但是执行这种查询会让应用和数据库都很繁忙,因为它需要不断的检查Spittle 的数量。
与重复查询数据库获得Spittle的数量相比,更好的方式是当这类事件发生时让MBean通知我 们。JMX通知(JMX notification,如图20.5 所示)是MBean与外部世界主动通信的一种方法,而 不是等待外部应用对MBean进行查询以获得信息。
Spring通过NotificationPublisherAware接口提供了发送通知的支持。任何希望发送通 知的MBean都必须实现这个接口。例如,请查看如下程序清单中的 SpittleNotifierImpl。
SpittleNotifierImpl实现了NotificationPublisherAware接口。这并不是一个要求苛刻的接口,它仅要求实现一个方 法:setNotificationPublisher。SpittleNotificationImpl也实现了SpittleNotifier接口的方 法:millionthSpittlePosted()。这个方法使用了setNotificationPublisher()方 法所注入的NotificationPublisher来发送通知,一旦sendNotification()方法被调用,就会发出通知。
package com.dxz.mvcdemo2.web.jmx.notification;public interface SpittleNotifier { public void millionthSpittlePosted();}package com.dxz.mvcdemo2.web.jmx.notification;import javax.management.Notification;import org.springframework.jmx.export.annotation.ManagedNotification;import org.springframework.jmx.export.annotation.ManagedResource;import org.springframework.jmx.export.notification.NotificationPublisher;import org.springframework.jmx.export.notification.NotificationPublisherAware;import org.springframework.stereotype.Component;@Component@ManagedResource("spittle:name=SpitterNotifier")@ManagedNotification(notificationTypes="SpitterNotifier.OneMillionSpittles", name="TODO")public class SpittleNotificationImpl implements NotificationPublisherAware, SpittleNotifier { //实现NotificationPublisherAware接口 private NotificationPublisher notificationPublisher; //注入notificationPublisher @Override public void setNotificationPublisher(NotificationPublisher notificationPublisher) { this.notificationPublisher = notificationPublisher; } public void millionthSpittlePosted() { //发送通知 notificationPublisher.sendNotification(new Notification("SpittleNotifier.OneMillionSpittles", this, 0)); }}
测试类:
package com.dxz.mvcdemo2.web.jmx.notification;import static org.springframework.web.bind.annotation.RequestMethod.GET;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;@Controller@RequestMapping("/biz5")public class SendNotifyController { @Autowired SpittleNotifier spittleNotifier; @RequestMapping(value = "/send", method = GET) public void send() { spittleNotifier.millionthSpittlePosted(); } }
启动测试:
jconsole连上JMX
点击通知:
浏览器多访问: 几次后,再看上面的页面变化如下:
监听通知
接收MBean通知的标准方法是实现javax.management.NotificationListener接口。 例如,考虑一下PagingNotificationListener:
package com.dxz.mvcdemo1.web.jmx.notification;import javax.management.Notification;import javax.management.NotificationListener;//@Component//@ManagedResource(objectName="spittle:name=PagingNotificationListener")public class PagingNotificationListener implements NotificationListener { @Override public void handleNotification(Notification notification, Object handback) { System.out.println(notification); }}
PagingNotificationListener是一个典型的JMX通知监听器。当接收到通知时,将会调 用handleNotification()方法处理通知。大概的逻辑可能是,PagingNotificationListener的handleNotification()方法将向寻呼机或手机上发送消息来告知Spittle数 量又到了一个新的百万级别(我把实际的实现留给读者自己完成)。 剩下的工作只需要使用MBeanExporter注册PagingNotificationListener:
@Bean public MBeanExporter mbeanExporter() { MBeanExporter exporter = new MBeanExporter(); /*Mapbeans = new HashMap (); beans.put("spittle:name=PagingNotificationListener", new PagingNotificationListener()); exporter.setBeans(beans);*/ Map mappings = new HashMap (); mappings.put("spittle:name=PagingNotificationListener", new PagingNotificationListener()); exporter.setNotificationListenerMappings(mappings); return exporter; }
MBeanExporter的notificationListenerMappings属性用于在监听器和监听器所希 望监听的MBean之间建立映射。在本示例中,我们建立了PagingNotificationListener 来监听由SpittleNotifier MBean所发布的通知。