通知
Spring的JMX功能包括对JMX通知的全面支持。
为通知注册监听器
Spring的JMX支持使得可以轻松地将任意数量的NotificationListeners注册到任意数量的MBeans上(这包括由Spring的MBeanExporter导出的MBeans以及通过其他机制注册的MBeans)。例如,考虑这样一种场景:每当目标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());
}
}
以下示例将ConsoleLoggingNotificationListener(在前一个示例中定义)添加到notificationListenerMappings中:
<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 bean就会收到通知。然后,ConsoleLoggingNotificationListener bean可以根据需要采取相应的操作来响应该Notification。
你也可以使用直接的bean名称作为导出的bean和listener之间的链接,如下例所示:
<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属性)。这次,我们不是为单个MBean配置一个NotificationListener,而是配置NotificationListenerBean实例。NotificationListenerBean封装了一个NotificationListener以及它要在MBeanServer中注册的ObjectName(或多个ObjectNames)。NotificationListenerBean还封装了其他一些属性,例如一个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时都能得到一个返回对象(handback object),并且还想通过提供一个NotificationFilter来过滤掉多余的Notifications。以下示例实现了这些目标:
<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>
<!-- implements both the NotificationListener and NotificationFilter interfaces -->
<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>
(关于“handback对象”是什么,以及“NotificationFilter”到底是什么的完整说明,请参阅JMX规范(1.2)中名为“JMX通知模型”(The JMX Notification Model)的部分。)
发布通知
Spring不仅提供了注册以接收通知(Notifications)的支持,还提供了发布通知的功能。
本节实际上仅与通过MBeanExporter暴露为MBeans的Spring管理的bean相关。任何现有的用户定义的MBeans应使用标准的JMX API来发布通知。
在Spring的JMX通知发布支持中,关键的接口是NotificationPublisher接口(定义在org.springframework.jmx.export.notification包中)。任何打算通过MBeanExporter实例作为MBean导出的bean都可以实现相关的NotificationPublisherAware接口,从而获得对NotificationPublisher实例的访问权限。NotificationPublisherAware接口通过一个简单的setter方法向实现该接口的bean提供一个NotificationPublisher实例,该bean随后可以使用这个实例来发布Notifications(通知)。
如NotificationPublisher接口的Javadoc中所述,通过NotificationPublisher机制发布事件的管理Bean并不负责通知监听器的状态管理。Spring的JMX支持会处理所有与JMX相关的基础设施问题。作为应用程序开发者,您所需要做的就是实现NotificationPublisherAware接口,然后使用提供的NotificationPublisher实例开始发布事件。需要注意的是,NotificationPublisher是在管理Bean被注册到MBeanServer之后才进行设置的。
使用NotificationPublisher实例非常简单。你创建一个JMX 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;
}
}
NotificationPublisher接口以及使其正常工作的机制,是Spring的JMX支持中较为出色的特性之一。然而,这也意味着你的类需要与Spring和JMX都发生耦合。一如既往,这里的建议是务实而为:如果你确实需要NotificationPublisher所提供的功能,并且能够接受与Spring和JMX的耦合,那么就使用它吧。