20.1 将 Spring bean 导出为 MBean

这里有几种方式可以让我们通过使用 JMX 来管理 Spittr 应用中的 bean。为了让事情尽量保持简单,我们对程序清单 5.10 中 SpittleController 只做适度的改变,增加一个新的 spittlesPerPage 属性:

public static final int DEFAULT_SPITTLES_PER_PAGE = 25;
private int spittlesPerPage = DEFAULT_SPITTLES_PER_PAGE;
public void setSpittlesPerPage(int spittlesPerPage) {
this.spittlesPerPage = spittlesPerPage;
}
public int getSpittlesPerPage() {
return spittlesPerPage;
}

之前,当我们调用 SpitterService 的 getRecentSpittles() 方法时,SpittleController 传入 20 作为第二个参数,这会查询最近的 20 条 Spittle。现在,不再是在构建应用时通过硬编码进行决策,而是通过使用 JMX 在运行时进行决策。新增的 spittlesPerPage 属性只是第一步而已。

但是 spittlesPerPage 属性本身并不能实现通过外部配置来改变页面上所显示 Spittle 的数量。它只是 bean 的一个属性,跟 bean 的其他属性一样。我们下一步需要做的是把 SpittleController bean 暴露为 MBean,而 spittlePerPage 属性将成为 MBean 的托管属性(managed attribute)。这时,我们就可以在运行时改变该属性的值。

Spring 的 MBeanExporter 是将 Spring Bean 转变为 MBean 的关键。MBeanExporter 可以把一个或多个 Spring bean 导出为 MBean 服务器(MBean server)内的模型 MBean。MBean 服务器(有时候也被称为 MBean 代理)是 MBean 生存的容器。对 MBean 的访问,也是通过 MBean 服务器来实现的。

如图 20.1 所示,将 Spring bean 导出为 JMX MBean 之后,可以使用基于 JMX 的管理工具(例如 JConsole 或者 VisualVM)查看正在运行的应用程序,显示 bean 的属性并调用 bean 的方法。

图 20.1 Spring 的 MBeanExporter 可以将 Spring bean 的属性和方法导出为 MBean 服务器中的 JMX 属性和操作。通过 JMX 服务器,JMX 管理工具(例如 JConsole)可以查看到正在运行的应用程序的内部情况

下面的 @Bean 方法在 Spring 中声明了一个 MBeanExporter,它会将 spittleController bean 导出为一个模型 MBean:

@Bean
public MBeanExporter mbeanExporter(SpittleController spittleController) {
MBeanExporter exporter = new MBeanExporter();
Map<String, Object> beans = new HashMap<String, Object>();
beans.put("spitter:name=SpittleController", spittleController);
exporter.setBeans(beans);
return exporter;
}

配置 MBeanExporte r的最简单方式是为它的 beans 属性配置一个 Map 集合,该集合中的元素是我们希望暴露为 JMX MBean 的一个或多个 bean。每个 Map 条目的 key 就是 MBean 的名称(由管理域的名字和一个 key-value 对组成,在 SpittleController MBean 示例中是 spitter:name=HomeController),而 Map 条目的值则是需要暴露的 Spring bean 引用。在这里,我们将输出 spittleController bean,以便它的属性可以通过 JMX 在运行时进行管理。 通过 MBeanExporter,spittleController bean 将作为模型 MBean 以 SpittleController 的名称导出到 MBean 服务器中,以实现管理功能。图 20.2 展示了通过 JConsole 查看 SpittleController MBean 时的情况。

图 20.2 SpittleController 导出为 MBean,并且可以通过 JConsole 查看

如图 20.2 的左侧所示,SpittleController 所有的 public 成员都被导出为 MBean 的操作或属性。这可能并不是我们所希望看到的结果,我们真正需要的只是可以配置 spittlesPerPage 属性。我们不需要调用 spittles() 方法或 SpittleController 中的其他方法或属性。因此,我们需要一个方式来筛选所需要的属性或方法。

为了对 MBean 的属性和操作获得更细粒度的控制,Spring 提供了几种选择,包括:

  • 通过名称来声明需要暴露或忽略的 bean 方法;

  • 通过为 bean 增加接口来选择要暴露的方法;

  • 通过注解标注 bean 来标识托管的属性和操作。

我们会尝试每一种方式来决定哪一种最适合 SpittleController MBean。我们首先通过名称来选择 bean 的哪些方法需要暴露。

MBean 服务器从何处而来

根据以上配置,MBeanExporter 会假设它正在一个应用服务器中(例如 Tomcat)或提供 MBean 服务器的其他上下文中运行。但是,如果 Spring 应用程序是独立的应用或运行的容器没有提供 MBean 服务器,我们就需要在 Spring 上下文中配置一个 MBean 服务器。

在 XML 配置中,<context:mbean-server> 元素可以为我们实现该功能。如果使用 Java 配置的话,我们需要更直接的方式,也就是配置类型为 MBeanServerFactoryBean 的 bean(这也是在 XML 中 <context:mbean-server> 元素所作的事情)。

MBeanServerFactoryBean 会创建一个 MBean 服务器,并将其作为 Spring 应用上下文中的 bean。默认情况下,这个 bean 的 ID 是 mbeanServer。了解到这一点,我们就可以将它装配到 MBeanExporter 的 server 属性中用来指定 MBean 要暴露到哪个 MBean 服务器中。