通知

Spring 的 JMX 提供全面的 JMX 通知支援。

為通知註冊監聽器

Spring 的 JMX 支援可以輕鬆地向任意數量的 MBean 註冊任意數量的 NotificationListener(包括由 Spring 的 MBeanExporter 匯出的 MBean 和透過其他機制註冊的 MBean)。例如,考慮這樣一種場景,每次目標 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>

有了前面的配置,每當 JMX Notification 從目標 MBean (bean:name=testBean1) 廣播時,透過 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 object)。

使用 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 觸發時都能獲得一個回執物件,並且還希望透過提供 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>

(有關回執物件是什麼以及 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 的耦合,那麼就這樣做。