通知
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 和监听器之间的链接,如下例所示:
<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 通知场景中使用的任意 handback 对象。
使用 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 对象,并且我们还希望通过提供一个 NotificationFilter
来过滤掉无关的 Notification
。以下示例实现了这些目标:
<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 通知模型”的部分。)
发布通知
Spring 不仅支持注册以接收 Notifications
,还支持发布 Notifications
。
本节仅与通过 MBeanExporter
暴露为 MBean 的 Spring 管理的 bean 相关。任何现有的用户定义 MBean 应该使用标准的 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 的耦合,那么就使用它。