Board logo

标题: 扩展 Spring 的 JMX 支持(3) [打印本页]

作者: look_w    时间: 2018-7-16 08:00     标题: 扩展 Spring 的 JMX 支持(3)

改进 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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 通知。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0