通知

Spring 的 JMX 產品提供了對 JMX 通知的全面支援。

註冊通知的 Listener

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>

在完成上述配置後,每次從目標 MBean (bean:name=testBean1) 廣播 JMX Notification 時,都會通知透過 notificationListenerMappings 屬性註冊為 Listener 的 ConsoleLoggingNotificationListener Bean。然後,ConsoleLoggingNotificationListener Bean 可以針對 Notification 採取其認為適當的任何動作。

您也可以使用直接 Bean 名稱作為匯出的 Bean 和 Listener 之間的連結,如下列範例所示

<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 屬性 Map 中條目的鍵,如下列範例所示

<property name="notificationListenerMappings">
	<map>
		<entry key="*">
			<bean class="com.example.ConsoleLoggingNotificationListener"/>
		</entry>
	</map>
</property>

如果您需要執行相反的操作(即,針對一個 MBean 註冊多個不同的 Listener),則必須改為使用 notificationListeners 列表屬性(優先於 notificationListenerMappings 屬性)。這次,我們不是為單個 MBean 配置 NotificationListener,而是配置 NotificationListenerBean 實例。NotificationListenerBean 封裝了 NotificationListener 及其要註冊到 MBeanServer 中的 ObjectName(或 ObjectNames)。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>
							<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 不負責通知 Listener 的狀態管理。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 的耦合,那就這樣做。