扩展 Spring 的 JMX 支持
改进 ModelMBean 中的通知
我要做的第一件事是扩展 Spring JMX ModelMBean 实现,这样就可以发送通知而不用在管理的资源中直接实现这个行为。为此,还需要扩展 Spring 导出器以创建改进的 ModelMBean 实例。最后,还需要用一个新的装配器从映射文件中提取 MBeanInfo 元数据。
ModelMBeanExtension 类
扩展 RequiredModelMBean 类的一个目的是在管理代理中透明地启用通知。这个应用程序需要三种通知:设置属性值、方法调用之前以及方法调用之后。因为消息是我自己配置的,在每一种情况下,它都可以按照我需要的那样提供信息。要实现这一点,我对类型通知使用了一个命名规范,其中对于样式 <matchingType>.<methodOrAttributeName> 检查点分隔的类型名。匹配的类型必须为 set、before 或者 after 之一。如果类型是 set,那么就认为是一个属性名,否则,就认为是一个方法名。
扩展的 ModelMBean 代码使用额外的类帮助进行耦合。第一个是 NotificationInfoMap,它是一个用通知元数据构建的、简单的 Map,并与前缀(set|before|after)点名(method|attribute)样式相关联,这样就可以更有效地得到匹配的通知元数据。第二个是工具方法的一个静态集合。清单 1 显示了为通知而扩展的 RequiredModelMBean:
清单 1. ModelMBeanExtension
因为 ModelMBean 已经是一种代理,所以不需要使用 Spring 的代理机制和 AOP 截获器来截获感兴趣的方法。ModelMBean 接口需要 setAttribute 和 invoke 方法的实现以管理对底层受管资源的调用。可以继承 RequiredModelMBean 类,保证它出现在所有 JMX 实现中,并增加我所需要的功能。
我的 ModelMBeanExtension 实现了同样的构造函数,但是在一个实例变量中存储了 ModelMBeanInfo 的一个副本。因为这个值可以通过构造函数或者调用 setModelMBeanInfo 方法设置,所以我覆盖了这个方法以存储这个值,调用超类以完成默认的行为。在默认情况下,RequiredModelMBean 类增加两个一般性通知描述符,因此我覆盖了 getNotificationInfo() 方法,只返回我描述的通知。仍然会发送一般性通知,但是要求特定通知的客户不会看到它们。
为了发送通知,我覆盖了 setAttribute() 和 invoke() 方法并检查调用是否匹配我的通知信息描述符。每次都遍历列表应该不会带来很大的开销,因为大多数类只会发送有限的一组通知,但是我需要测试每一个通知可能的许多通知类型字符串,而重复这一过程看来是个浪费。为了保证不会遇到性能问题,我实例化了一个通知信息映射,这是一个名称/信息映射,可以用来进行快速查询。关键是一个具有类型前缀(set、before 或者 after)和所涉及的属性和方法的简单字符串。可以使用 findNotificationInfo() 方法在 setAttribute() 调用或者方法调用时查找通知信息实例。
完成了基础架构后,就可以截获对 setAttribute() 和 invoke() 方法的调用了。属性改变需要发送一个 AttributeChangeNotification 实例,它要求有旧的属性值和新的值,以及从通知信息描述符中可以得到的细节。在发送通知时,如果消息顺序是混乱的,则要发送序列号,让客户机应用程序可以对消息排序。为了简化,我使用了当前时间戳而不是管理一个计数器。创建通知对象时,sendNotification() 方法保证它会发布。对于 invoke() 方法使用同样的思路,尽管在这里我使用了更简单的 Notification 对象。可以调用超类中的 invoke() 方法同时检查这两者(before 和 after),并根据查找结果发送 before 和 after 通知。
我要做的第一件事是扩展 Spring JMX ModelMBean 实现,这样就可以发送通知而不用在管理的资源中直接实现这个行为。为此,还需要扩展 Spring 导出器以创建改进的 ModelMBean 实例。最后,还需要用一个新的装配器从映射文件中提取 MBeanInfo 元数据。
ModelMBeanExtension 类
扩展 RequiredModelMBean 类的一个目的是在管理代理中透明地启用通知。这个应用程序需要三种通知:设置属性值、方法调用之前以及方法调用之后。因为消息是我自己配置的,在每一种情况下,它都可以按照我需要的那样提供信息。要实现这一点,我对类型通知使用了一个命名规范,其中对于样式 <matchingType>.<methodOrAttributeName> 检查点分隔的类型名。匹配的类型必须为 set、before 或者 after 之一。如果类型是 set,那么就认为是一个属性名,否则,就认为是一个方法名。
扩展的 ModelMBean 代码使用额外的类帮助进行耦合。第一个是 NotificationInfoMap,它是一个用通知元数据构建的、简单的 Map,并与前缀(set|before|after)点名(method|attribute)样式相关联,这样就可以更有效地得到匹配的通知元数据。第二个是工具方法的一个静态集合。清单 1 显示了为通知而扩展的 RequiredModelMBean:
清单 1. ModelMBeanExtension
不需要代理代理!package com.claudeduguay.mbeans.spring;
![]()
import java.lang.reflect.*;
![]()
import javax.management.*;
import javax.management.modelmbean.*;
![]()
public class ModelMBeanExtension extends RequiredModelMBean
...{
protected NotificationInfoMap notificationInfoMap;
protected ModelMBeanInfo modelMBeanInfo;
protected Object managedBean;
![]()
public ModelMBeanExtension() throws MBeanException ...{}
![]()
public ModelMBeanExtension(ModelMBeanInfo modelMBeanInfo)
throws MBeanException
...{
super(modelMBeanInfo);
this.modelMBeanInfo = modelMBeanInfo;
notificationInfoMap = new NotificationInfoMap(modelMBeanInfo);
}
![]()
public void setModelMBeanInfo(ModelMBeanInfo modelMBeanInfo)
throws MBeanException
...{
this.modelMBeanInfo = modelMBeanInfo;
notificationInfoMap = new NotificationInfoMap(modelMBeanInfo);
super.setModelMBeanInfo(modelMBeanInfo);
}
![]()
public MBeanNotificationInfo[] getNotificationInfo()
...{
return modelMBeanInfo.getNotifications();
}
![]()
public void setManagedResource(Object managedBean, String type)
throws MBeanException, RuntimeOperationsException,
InstanceNotFoundException, InvalidTargetObjectTypeException
...{
super.setManagedResource(managedBean, type);
this.managedBean = managedBean;
}
![]()
protected void maybeSendMethodNotification(
String type, String name) throws MBeanException
...{
MBeanNotificationInfo info = notificationInfoMap.
findNotificationInfo(type, name);
if (info != null)
...{
long timeStamp = System.currentTimeMillis();
String notificationType = ModelMBeanUtil.
matchType(info, "." + type + "." + name);
sendNotification(new Notification(
notificationType, this, timeStamp,
info.getDescription()));
}
}
![]()
protected void maybeSendAttributeNotification(
Attribute attribute)
throws MBeanException, AttributeNotFoundException,
InvalidAttributeValueException, ReflectionException
...{
String name = attribute.getName();
MBeanNotificationInfo info = notificationInfoMap.
findNotificationInfo("set", attribute.getName());
if (info != null)
...{
Object oldValue = getAttribute(name);
Object newValue = attribute.getValue();
long timeStamp = System.currentTimeMillis();
String notificationType = ModelMBeanUtil.
matchType(info, ".set." + name);
sendNotification(new AttributeChangeNotification(
this, timeStamp, timeStamp,
info.getDescription(), info.getName(),
notificationType, oldValue, newValue));
}
}
![]()
public Object invoke(
String name, Object[] args, String[] signature)
throws MBeanException, ReflectionException
...{
maybeSendMethodNotification("before", name);
Object returnValue = super.invoke(name, args, signature);
maybeSendMethodNotification("after", name);
return returnValue;
}
![]()
public Object getAttribute(String name) throws MBeanException,
AttributeNotFoundException, ReflectionException
...{
try
...{
Method method = ModelMBeanUtil.findGetMethod(
modelMBeanInfo, managedBean, name);
return method.invoke(managedBean, new Object[] ...{});
}
catch (IllegalAccessException e)
...{
throw new MBeanException(e);
}
catch (InvocationTargetException e)
...{
throw new MBeanException(e);
}
}
![]()
public void setAttribute(Attribute attribute)
throws MBeanException, AttributeNotFoundException,
InvalidAttributeValueException, ReflectionException
...{
try
...{
Method method = ModelMBeanUtil.findSetMethod(
modelMBeanInfo, managedBean, attribute.getName());
method.invoke(managedBean, attribute.getValue());
maybeSendAttributeNotification(attribute);
}
catch (InvocationTargetException e)
...{
throw new MBeanException(e);
}
catch (IllegalAccessException e)
...{
throw new MBeanException(e);
}
}
}
因为 ModelMBean 已经是一种代理,所以不需要使用 Spring 的代理机制和 AOP 截获器来截获感兴趣的方法。ModelMBean 接口需要 setAttribute 和 invoke 方法的实现以管理对底层受管资源的调用。可以继承 RequiredModelMBean 类,保证它出现在所有 JMX 实现中,并增加我所需要的功能。
我的 ModelMBeanExtension 实现了同样的构造函数,但是在一个实例变量中存储了 ModelMBeanInfo 的一个副本。因为这个值可以通过构造函数或者调用 setModelMBeanInfo 方法设置,所以我覆盖了这个方法以存储这个值,调用超类以完成默认的行为。在默认情况下,RequiredModelMBean 类增加两个一般性通知描述符,因此我覆盖了 getNotificationInfo() 方法,只返回我描述的通知。仍然会发送一般性通知,但是要求特定通知的客户不会看到它们。
为了发送通知,我覆盖了 setAttribute() 和 invoke() 方法并检查调用是否匹配我的通知信息描述符。每次都遍历列表应该不会带来很大的开销,因为大多数类只会发送有限的一组通知,但是我需要测试每一个通知可能的许多通知类型字符串,而重复这一过程看来是个浪费。为了保证不会遇到性能问题,我实例化了一个通知信息映射,这是一个名称/信息映射,可以用来进行快速查询。关键是一个具有类型前缀(set、before 或者 after)和所涉及的属性和方法的简单字符串。可以使用 findNotificationInfo() 方法在 setAttribute() 调用或者方法调用时查找通知信息实例。
完成了基础架构后,就可以截获对 setAttribute() 和 invoke() 方法的调用了。属性改变需要发送一个 AttributeChangeNotification 实例,它要求有旧的属性值和新的值,以及从通知信息描述符中可以得到的细节。在发送通知时,如果消息顺序是混乱的,则要发送序列号,让客户机应用程序可以对消息排序。为了简化,我使用了当前时间戳而不是管理一个计数器。创建通知对象时,sendNotification() 方法保证它会发布。对于 invoke() 方法使用同样的思路,尽管在这里我使用了更简单的 Notification 对象。可以调用超类中的 invoke() 方法同时检查这两者(before 和 after),并根据查找结果发送 before 和 after 通知。
0
相关文章
