通过查询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
点击通知:
浏览器多访问:http://localhost:8011/biz5/send 几次后,再看上面的页面变化如下:
监听通知
接收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(); /*Map<String, Object> beans = new HashMap<String, Object>(); beans.put("spittle:name=PagingNotificationListener", new PagingNotificationListener()); exporter.setBeans(beans);*/ Map<String, NotificationListener> mappings = new HashMap<String, NotificationListener>(); mappings.put("spittle:name=PagingNotificationListener", new PagingNotificationListener()); exporter.setNotificationListenerMappings(mappings); return exporter; }
MBeanExporter的notificationListenerMappings属性用于在监听器和监听器所希 望监听的MBean之间建立映射。在本示例中,我们建立了PagingNotificationListener 来监听由SpittleNotifier MBean所发布的通知。