通知

Infra JMX 产品包括对 JMX 通知的全面支持。

注册通知监听器

Infra JMX 支持使得向任意数量的 MBean 注册任意数量的 NotificationListeners 变得容易(这包括由 Infra 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="infra.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="infra.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="infra.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="infra.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 属性)。这一次,我们配置 NotificationListenerBean 实例,而不是为单个 MBean 配置 NotificationListenerNotificationListenerBean 封装了一个 NotificationListener 和它要在 MBeanServer 中注册的 ObjectName(或多个 ObjectName)。NotificationListenerBean 还封装了许多其他属性,例如 NotificationFilter 和可以在高级 JMX 通知场景中使用的任意回传对象。

使用 NotificationListenerBean 实例时的配置与前面介绍的并没有太大的不同,如下例所示:

<beans>

  <bean id="exporter" class="infra.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="bean:name=testBean1" value-ref="testBean"/>
      </map>
    </property>
    <property name="notificationListeners">
      <list>
        <bean class="infra.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="infra.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

</beans>

前面的示例等同于第一个通知示例。那么,假设我们希望在每次发出 Notification 时都获得一个回传对象,并且我们还希望通过提供 NotificationFilter 来过滤掉无关的 Notifications。以下示例实现了这些目标:

<beans>

  <bean id="exporter" class="infra.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="infra.jmx.export.NotificationListenerBean">
          <constructor-arg ref="customerNotificationListener"/>
          <property name="mappedObjectNames">
            <list>
              <!-- 处理来自两个不同 MBean 的通知 -->
              <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="infra.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

  <bean id="testBean2" class="infra.jmx.JmxTestBean">
    <property name="name" value="ANOTHER TEST"/>
    <property name="age" value="200"/>
  </bean>

</beans>

(有关什么是回传对象以及什么是 NotificationFilter 的完整讨论,请参阅 JMX 规范 (1.2) 中题为“JMX 通知模型”的部分。)

发布通知

Infra 不仅支持注册接收 Notifications,还支持发布 Notifications

本节实际上仅与已通过 MBeanExporter 公开为 MBean 的 Infra 托管 Bean 相关。任何现有的用户定义的 MBean 都应使用标准 JMX API 进行通知发布。

Infra JMX 通知发布支持的关键接口是 NotificationPublisher 接口(定义在 infra.jmx.export.notification 包中)。任何将通过 MBeanExporter 实例导出为 MBean 的 Bean 都可以实现相关的 NotificationPublisherAware 接口,以获得对 NotificationPublisher 实例的访问权限。NotificationPublisherAware 接口通过一个简单的 setter 方法向实现 Bean 提供 NotificationPublisher 的实例,然后 Bean 可以使用该实例发布 Notifications

NotificationPublisher 接口的 javadoc 中所述,通过 NotificationPublisher 机制发布事件的托管 Bean 不负责通知监听器的状态管理。Infra JMX 支持负责处理所有 JMX 基础设施问题。作为应用程序开发人员,您需要做的就是实现 NotificationPublisherAware 接口,并开始使用提供的 NotificationPublisher 实例发布事件。请注意,NotificationPublisher 是在托管 Bean 向 MBeanServer 注册后设置的。

使用 NotificationPublisher 实例非常简单。您创建一个 JMX Notification 实例(或适当的 Notification 子类的实例),使用与要发布的事件相关的数据填充通知,并在 NotificationPublisher 实例上调用 sendNotification(Notification),传入 Notification

在以下示例中,JmxTestBean 的导出实例每当调用 add(int, int) 操作时都会发布 NotificationEvent

package infra.jmx;

import infra.jmx.export.notification.NotificationPublisherAware;
import infra.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;

  // 为清晰起见省略了其他 getter 和 setter

  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 接口以及使其全部工作的机制是 Infra JMX 支持的较好功能之一。然而,它确实伴随着将您的类与 Infra 和 JMX 耦合的代价。一如既往,这里的建议是务实。如果您需要 NotificationPublisher 提供的功能,并且可以接受与 Infra 和 JMX 的耦合,那么就使用它。