通知

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>

如果您想要为MBean注册单个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来过滤掉多余的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>
							<!-- 处理来自两个不同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>

	<!-- 实现NotificationListener和NotificationFilter接口 -->
	<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将Spring管理的bean暴露为MBeans相关。任何现有的用户定义的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

在以下示例中,JmxTestBean的导出实例在每次调用add(int, int)操作时发布一个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的耦合,那就这样做。