使用JMX管理Spring Bean
学习内容
将Spring bean暴露为MBean
远程管理Spring Bean
处理JMX通知
应用背景
Spring对DI的支持是通过在应用中配置Bean属性,这是一种非常不错的方法。不过,一旦应用已经部署并且正在运行,单独使用DI并不能帮助我们改变应用的配置。假设我们希望深入了解正在运行的应用并要在运行时改变应用的配置,此时,就可以使用java管理扩展(Java Manage-ment Extensions JMX)。
JMX这项技术能够让我们管理、监视和配置应用。
使用JMX管理应用的核心组件是托管bean(managed bean,MBean)。所谓的MBean就是暴露特定方法到JavaBean,这些方法定义了管理接口。JMX规范定义了如下4种类型的MBean:
标准MBean:标准MBean的管理接口是通过在固定的接口上执行反射确定的,bean类会实现这个接口;
动态MBean:动态MBean的管理接口是在运行时通过调用DynamicMBean接口的方法来确定的。因为管理接口不是通过静态接口定义的,因此可以在运行时改变;
开放MBean:开放MBean是一种特殊的动态MBean,其属性和方法只限定于原始类型、原始类型的包装类以及可以分解为原始类型或原始类型包装类的任意类型;
模型MBean:模型MBean也是一种特殊的动态MBean,用于充当管理接口与受管资源的中介。模型Bean并不像它们所声明的那样来编写。它们通常通过工厂生成,工厂会使用元信息来组装管理接口
Spring的JMX模块可以让我们将Spring bean导出为模型MBean,这样我们就可以查看应用程序的内部情况并且能够更改配置--甚至在应用的运行期。
将Spring bean导出为MBean
Spring的MBeanExporter是将Spring Bean转变为MBean的关键。MBeanExporter可以把一个或多个Spring bean导出为MBean服务器(MBean server)内的模型MBean。MBean服务器是MBean生存的容器。对MBean的访问,也是通过MBean服务器来实现的。
下面的@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=Controller", spittleController);
exporter.setBeans(beans);
return exporter;
}
配置MBeanExporter的最简单方式是为他的beans属性配置一个Map集合,该集合中的元素是我们希望暴露为JMX MBean的一个或多个bean。每个Map条目的key就是Mbean的名称(由管理域的名字和一个key-value对组成,在SpittleController MBean示例中是spitter:name=Controller),而Map条目的值则是需要暴露的Spring bean引用。
为了对MBean的属性和操作获得更细粒度的控制,Spring提供了几种选择,包括:
通过名称来声明需要暴露或忽略的bean方法;
通过为bean增加接口来选择要暴露的方法;
通过注解标注bean来标识托管的属性和操作。
MBean的服务器从何处而来
如果没有提供MBean服务器,我们就需要在Spring上下文中配置一个MBean服务器
在XML配置中,<Context:mbean-server>元素可以为我们实行该功能。
如果使用Java配置的话,我们需要更直接的方式,也就是配置类型位MBeanServerFactoryBean的bean。
MBeanServerFactoryBean会创建一个MBean服务器,并将其作为Spring应用上下文中的bean。默认情况下,这个bean的ID是mbeanServer。了解到这一点,我们就可以将他装配到MbeanExporter的server属性中用来指定Mbean要暴露到那个Mbean服务器中。
1、通过名称暴露方法
Mbean信息装配器时限制那些方法和属性将在Mbean上暴露的关键。
MethodNameBasedMBeanInfoAssembler,这个装配器指定了需要暴露为Mbean操作的方法名称列表。Java代码配置如下:
@Bean
public MBeanExporter mBeanExporter(SpittleController spittleController) {
MBeanExporter exporter = new MBeanExporter();
Map<String, Object> beans = new HashMap<String, Object>();
beans.put("spitter:name=Controller", spittleController);
exporter.setBeans(beans);
exporter.setAssembler(assembler());
return exporter;
}
@Bean
public MethodNameBasedMBeanInfoAssembler assembler() {
MethodNameBasedMBeanInfoAssembler assembler =
new MethodNameBasedMBeanInfoAssembler();
assembler.setManagedMethods(new String[] {
"getSpitlesPerPage", "setSpitlesPerPage"
});
return assembler;
}
MethodExclusionMBeanInfoAssembler是用于指定不需要暴露为Mbean托管操作的方法名称列表。
2、使用接口定义MBean的操作和属性
InterfaceBasedMBeanInfoAssembler是另一种MBean信息装配器,可以让我们通过使用接口来选择bean的那些方法需要暴露为MBean的托管操作。
@Bean
public MBeanExporter mBeanExporter(SpittleController spittleController) {
MBeanExporter exporter = new MBeanExporter();
Map<String, Object> beans = new HashMap<String, Object>();
beans.put("spitter:name=Controller", spittleController);
exporter.setBeans(beans);
exporter.setAssembler(interfaceAssembler());
return exporter;
}
@Bean
public InterfaceBasedMBeanInfoAssembler interfaceAssembler() {
InterfaceBasedMBeanInfoAssembler assembler =
new InterfaceBasedMBeanInfoAssembler();
assembler.setManagedInterfaces(
new Class[] {ControllerOperation.class});
return assembler;
}
public interface ControllerOperation {
public int getSpitlesPerPage();
public void setSpitlesPerPage(int spitlesPerPage);
public List<Spittle> spittles(
long max,
int count);
}
3、使用注解驱动的MBean
注解配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:mbean-export server="mbeanServer"/>
</beans>
@Controller
@RequestMapping("/spittles")
@ManagedResource(objectName="spitter:name=Controller")
public class SpittleController {
private static final int DEFAULT_PER_PAGE = 25;
private int spitlesPerPage = DEFAULT_PER_PAGE;
@ManagedAttribute
public int getSpitlesPerPage() {
return spitlesPerPage;
}
@ManagedAttribute
public void setSpitlesPerPage(int spitlesPerPage) {
this.spitlesPerPage = spitlesPerPage;
}
}
4、处理MBean冲突
@Bean
public MBeanExporter mBeanExporter(SpittleController spittleController) {
MBeanExporter exporter = new MBeanExporter();
Map<String, Object> beans = new HashMap<String, Object>();
beans.put("spitter:name=Controller", spittleController);
exporter.setBeans(beans);
exporter.setAssembler(interfaceAssembler());
exporter.setRegistrationPolicy(RegistrationPolicy.IGNORE_EXISTING);
return exporter;
}
远程MBean
。。。。。。。。。。。。。。。。
处理通知
JMX通知是MBean与外部世界主动通信的一种方法,而不是等待外部应用对MBean进行查询以获得信息。
1、发送通知
Spring通过NotificationPublisherAware接口提供了发送通知的支持。任何希望发送通知的MBean都必须实现这个接口。
@Controller
@RequestMapping("/spittles")
@ManagedResource(objectName="spitter:name=Controller")
@ManagedNotification(notificationTypes="SpittleNotifier.OneMillion", name="TODO")
public class SpittleController implements NotificationPublisherAware, SpittleNotifier{
private NotificationPublisher notificationPublisher;
@Override
public void setNotificationPublisher(NotificationPublisher notificationPublisher) {
// TODO Auto-generated method stub
this.notificationPublisher = notificationPublisher;
}
@Override
public void millionPosted() {
// TODO Auto-generated method stub
notificationPublisher.sendNotification(
new Notification("SpittleNotifier.OneMillion", this, 0));
}
}
2、监听通知
接收Mbean通知的标注方法是实现javax.management.NotificationListener接口。
还需要将监听器注册到MbeanExporter(setNotificationListenerMappings)