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的耦合,那么就去做。