Spring的JMX提供的内容包括了对JMX通知的全面的支持。
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" 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
。
如果想给所有的正在输出的已经装入MBeanExporter
的bean注册单个
NotificationListener
实例,可以用特殊的通配符'*'
(没有引号)
作为属性映射notificationListenerMappings
的一个实体的键,例如:
<property name="notificationListenerMappings"> <map> <entry key="*"> <bean class="com.example.ConsoleLoggingNotificationListener"/> </entry> </map> </property>
如果想与上面相反(即,为一个MBean注册多个不同的监听器),那么就必须使用 notificationListeners
这个列表属性来代替(优先于属性notificationListenerMappings
)。
这次,要配置多个 NotificationListenerBean
实例,而不是简单的为单个MBean配置一个
NotificationListener
……一个 NotificationListenerBean
封装了一个 NotificationListener
和 ObjectName
(或ObjectNames
)
这样,它就在一个 MBeanServer
里进行注册。
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> <bean class="javax.management.ObjectName"> <constructor-arg value="bean:name=testBean1"/> </bean> </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
。
(要全面的讨论什么是一个回传对象,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> <!-- let's handle notifications from two distinct MBeans --> <bean class="javax.management.ObjectName"> <constructor-arg value="bean:name=testBean1"/> </bean> <bean class="javax.management.ObjectName"> <constructor-arg value="bean:name=testBean2"/> </bean> </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>
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的耦合,那么就去做。