MXBean跟标准MBean很像,标准MBean需要实现XXXXMBean这样命名的接口,而MXBean则需要实现XXXXMXBean这样命名的接口,也可以在接口上使用注解@MXBean,而不用强制使用XXXMXBean这样的命名格式。但是MXBean有点在于它可以供任何的client,包括remote client访问相关属性和执行相关操作。并且client不需要具有MXBean类(e.g. 在JConsole中,MBean类型也可以供remote client访问,基本类型是可以展示的,但是一旦有复杂类型,那就不能显示了)。为了满足这种机制,JMX提供了一套Open type-Open value用于双方交互。以使耦合度减少。VM的很多属性都是通过MXBean的形式提供的。
例子:
代码:ZooMXBean,MXBean接口 1 package test.jmx.mxbean.simple;
2
3 public interface ZooMXBean {
4
5 public Tiger getTiger();
6
7 public void addTiger(Tiger tiger);
8
9 public String getZooName();
10
11 public int getTigerCount();
12 }
13
代码:ZooImpl,MXBean的实现类 1 package test.jmx.mxbean.simple;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 public class ZooImpl implements ZooMXBean {
7
8 private String zooName = " China zoo";
9 private static List<Tiger> list;
10 static {
11 //初始化一只Tiger
12 Tiger tiger = new Tiger(" the first tiger");
13 list = new ArrayList<Tiger>();
14 list.add(tiger);
15 }
16 public void addTiger(Tiger tiger) {
17 list.add(tiger);
18 }
19
20 public Tiger getTiger() {
21 return list.get(0);
22 }
23
24 public int getTigerCount(){
25 return list.size();
26 }
27
28 public String getZooName() {
29 return zooName;
30 }
31
32 public String[] getAnimalNames(){
33 return new String[]{"bird","tiger","mouse"};
34 };
35 }
36
代码:Tiger,复杂的类型(不同于java基本类型) 1 package test.jmx.mxbean.simple;
2
3 import java.beans.ConstructorProperties;
4
5
6 public class Tiger {
7
8 private String name;
9 @ConstructorProperties({})
10 public Tiger(){
11 this.name = "the default constructor";
12 }
13
14 @ConstructorProperties({"name"})
15 public Tiger(String name){
16 this.name = name;
17 }
18
19 public String getName(){
20 return name;
21 }
22
23 public String roar(){
24 return "@¥%%……";
25 }
26
27 public void setName(String name){
28 this.name=name;
29 }
30 public String[] getFoodNames(){
31 return new String[]{"rabbit","sheep","pig"};
32 }
33 }
34
代码:Server 1 package test.jmx.mxbean.simple;
2 3 import java.lang.management.ManagementFactory;
4 import java.rmi.registry.LocateRegistry;
5 6 import javax.management.MBeanServer;
7 import javax.management.ObjectName;
8 import javax.management.remote.JMXConnectorServer;
9 import javax.management.remote.JMXConnectorServerFactory;
10 import javax.management.remote.JMXServiceURL;
11 12 public class Server {
13 public static void main(String args[])
throws Exception{
14 15 // MBeanServer mbs = MBeanServerFactory.createMBeanServer();
16 MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
17 LocateRegistry.createRegistry(9999);
18 JMXServiceURL url =
new JMXServiceURL(
19 "service:jmx:rmi:///jndi/rmi://localhost:9999/server");
20 JMXConnectorServer cs = JMXConnectorServerFactory
21 .newJMXConnectorServer(url,
null, mbs);
22 23 ZooMXBean mxbean =
new ZooImpl();
24 ObjectName name =
new ObjectName("ZooMXBean:type=MXBean");
25 //注册ZooOpenMBean这个OpenMBean
26 mbs.registerMBean(mxbean, name);
27 //开起RMI服务
28 cs.start();
29 30 System.out.println(" the mxbean server is start
");
31 }
32 }
33 代码:Client端 1 2 package test.jmx.mxbean.simple;
3 4 5 6 import javax.management.MBeanServerConnection;
7 import javax.management.ObjectName;
8 import javax.management.openmbean.ArrayType;
9 import javax.management.openmbean.CompositeData;
10 import javax.management.openmbean.CompositeDataSupport;
11 import javax.management.openmbean.CompositeType;
12 import javax.management.openmbean.OpenType;
13 import javax.management.openmbean.SimpleType;
14 import javax.management.remote.JMXConnector;
15 import javax.management.remote.JMXConnectorFactory;
16 import javax.management.remote.JMXServiceURL;
17 18 public class Client {
19 20 public static void main(String[] args)
throws Exception{
21 22 //构造一个Rmi-Connector
23 JMXServiceURL url =
new JMXServiceURL(
24 "service:jmx:rmi:///jndi/rmi://localhost:9999/server");
25 JMXConnector jmxc = JMXConnectorFactory.connect(url,
null);
26 MBeanServerConnection msc = jmxc.getMBeanServerConnection();
27 28 ObjectName name =
new ObjectName("ZooMXBean:type=MXBean");
29 30 Object tiger = msc.getAttribute(name, "Tiger");
31 if(tiger
instanceof CompositeData){
32 System.out.println("返回的Tiger的类型为CompositeData");
33 CompositeData data = (CompositeData)tiger;
34 String nm = (String)(data.get("name"));
35 String[] foods = (String[])(data.get("foodNames"));
36 System.out.println(" the tiger's name is :"+nm);
37 System.out.println(" the tiger's foods is :"+foods);
38 }
39 40 Integer count1 = (Integer)msc.getAttribute(name, "TigerCount");
41 System.out.println(" the amount of tiger is:"+count1);
42 43 //构造一个CompositeData代表Tiger实例,用于addTiger(Tiger)的参数
44 CompositeType ct2 =
new CompositeType("test.jmx.mxbean.Tiger", " tiger---",
45 new String[]{"name","foodNames"},
46 new String[]{"-name-","-foods-"},
47 new OpenType[]{SimpleType.STRING,
new ArrayType(1,SimpleType.STRING)});
48 49 CompositeData ct2V =
new CompositeDataSupport(ct2,
50 new String[]{"name","foodNames"},
51 new Object[]{"the second tiger",
new String[]{"food1","food2","food3"}});
52 53 Object returnValue = msc.invoke(name, "addTiger",
54 new Object[]{ct2V},
55 new String[]{CompositeData.
class.getName()});
56 //得到服务端Tiger的数量,新增了以后,应该是2只
57 Integer count2 = (Integer)msc.getAttribute(name, "TigerCount");
58 System.out.println(" after invoke addTiger(
),the amount of tiger is:"+count2);
59 }
60 }
61 上面例子中,我们自定义了ZooMXBean就是MXBean接口,ZooImpl是其实现类;Tiger为自定义的一个Java类;Server为MBeanServer所在的服务端,可以使用JDK自带的jconsole查看MXBean属性;Client端,主要是验证Tiger是如何转化成Open Type并在Server-Clinet两端操作的。
我们可以通过jconsole查看这个注册的MXBean。
图:ZooMXBean属性图:Tiger属性 Jconsole控制台就是JMX兼容的监视工具。它使用Java虚拟机的JMX机制来提供运行在Java平台的应用程序的各种信息。在上图中我们可以看出属性中Tiger值是CompositeDataSupport。为什么我们在ZooXMBean接口中定义的getTiger()方法,也即属性Tiger的值为Tiger类的实例。但是jconsole平台显示的确是一个CompositeDataSupport呢。首先在jconsole这边是没有Tiger这个类的,即jconsole这端是不可能实例化出一个Tiger类型的实例的。但是CompositeDataSupport(父类:CompositeData)在JMX机制中是属于Open Data。说到Open Data就得说下JMX 中的OpenMBean了,在OpenMBean中为了实现但新增一个'MBean',的时候Manager Application可以再运行时能发现这个新增'MBean',管理员能知道这个'MBean'的意思,和如何操作;并且不需要重新编译。为了实现上述功能,JMX中有一套Open Type-Open Value集合。Manager Application与Agent之间的通信需要使用这套类型。正式应该在Manager Application中也知道CompoisteData的结构,故能从中取得相应的数据,而不需要知道Tiger类。
表格1:Open type和Java Type对应关系
Java Type | Open Type | Open Value |
java.lang.Void | SimpleType.Void | \ |
java.lang.Boolean | SimpleType.Boolean | java.lang.Boolean |
java.lang.Character | SimpleType.Character | java.lang.Character |
java.lang.Byte | SimpleType.Byte | java.lang.Byte |
java.lang.Short | SimpleType.Short | java.lang.Short |
java.lang.Integer | SimpleType.Integer | java.lang.Integer |
java.lang.Long | SimpleType.Long | java.lang.Long |
java.lang.Float | SimpleType.Float | java.lang.Float |
java.lang.Double | SimpleType.Double | java.lang.Double |
java.lang.String | SimpleType.String | java.lang.String |
java.math.BigDecimal | SimpleType.BigDecimal | java.math.BigDecimal |
java.math.BigInteger | SimpleType.BigInteger | java.math.BigInteger |
java.util.Date | SimpleType.Date | java.util.Date |
javax.management.ObjectName | javax.management.ObjectName | javax.management.ObjectName |
javax.management.openmbean.CompositeType | javax.management.openmbean.CompositeType | CompositeData |
javax.management.openmbean.TabularType | javax.management.openmbean.TabularType | TabularData |
| javax.management.openmbean.ArrayType | 以上Open value的数组形式,任意维度 |
Clinet与Server交互的流程: 1. Server端注册一个MXBean的时候,JMX内部会通过MXBeanMappingFactory.mappingForType(Type t, MXBeanMappingFactory f)方法建立起Java type 到Open Type的映射关系。
2. client客户端执行getAttribute(...)或者invoke(...)操作的时候。需要传递一个Open Type的参数。想ZooMXBean.addTiger(Tiger)需要一个参数,但是client端,是没有Tiger类的,这时候就需要构造一个CompositeType类型的值CompositeData传递给Server。
3. Server收到传递过来的Open Type参数,通过MXBeanMapping.fromOpenValue(Object openValue)把Open Type类型的参数Open Value转化到Java Type类型的Java Value实例(i.e. 如何参数为代表Tiger的CompositeData,那么MXBeanMapping就会通过这个CompositeData,构造出一个真正的Tiger实例)。
4. Server端调用MXBean方法,得到一个Java Type的返回值。如果有返回值,那么就会通过MXBeanMapping.toOpenValue(Object javaValue)把Java Value转换成Open Value。传递成client。
5. server-client端对于Open-Type的机制都是知道的。于是client就可以在得到的Open Value中得到想要的数据(不需要server端自定义类的字节码,如Tiger)。
MXBeanMapping、MXBeanMappingFactory、DefaultMXBeanMappingFactory图:MXBeanMappingFactory、MXBeanMapping结构 MXBeanMapping用于Open Type-Java Type的映射,使它们可以相互转化。MXBeanMappingFactory.mappingForType(Type t, MXBeanMappingFactory f)创建MXBeanMapping。DefaultMXBeanMappingFactory是MXBeanMappingFactory实现类,而MXBeanMapping的实现类是作为DefaultMXBeanMappingFactory内部类。这些类是在JDK7中的sun包中的。不同的JDK可能实现不一样,类名也可能不存在。
ConvertingMethod主要用于在执行MXBean中的方法前后,对参数或者返回值进行转化。
1 //MBserServer对MXBean的操作,最终都是执行MXBean接口里面的方法,而ConvertingMethod就是对MXBean里面的方法,进行包装。
//把调用者的Open Type参数,转化成Java Type;并且接口中方法的Java Type的返回值转换成Open Type返回给调用者
2 ConvertingMethod:
3 //参数m其实就是MXBean接口中定义的方法,也就是需要MBeanServer管理的属性和操作。这个方法用于对m进行相关的包装、转换。m中的参数、返回值都跟Open Type建立映射关系。
//通过源码发现,MXBean在被注册的时候,会调用此方法。既MXBean中自定义的属性、参数类型就是在这里更Open Type建立映射关系的
4 static ConvertingMethod from(Method m) {
5 try {
6 return new ConvertingMethod(m);
7 } catch (OpenDataException ode) {
8 final String msg = "Method " + m.getDeclaringClass().getName() +
9 "." + m.getName() + " has parameter or return type that " +
10 "cannot be translated into an open type";
11 throw new IllegalArgumentException(msg, ode);
12 }
13 }
14
15 private ConvertingMethod(Method m) throws OpenDataException {
16 this.method = m;
17 MXBeanMappingFactory mappingFactory = MXBeanMappingFactory.DEFAULT;
18 //把m方法的返回值类型映射到Open Type,得到映射关系
19 returnMapping = mappingFactory.mappingForType(m.getGenericReturnType(), mappingFactory);
20 //得到m里面的所有参数类型
21 Type[] params = m.getGenericParameterTypes();
22 paramMappings = new MXBeanMapping[params.length];
23 boolean identity = true;
24 for (int i = 0; i < params.length; i++) {
25 //把m的参数类型也映射到Open Type,得到映射关系
26 paramMappings[i] = mappingFactory.mappingForType(params[i], mappingFactory);
27 identity &= DefaultMXBeanMappingFactory.isIdentity(paramMappings[i]);
28 }
29 paramConversionIsIdentity = identity;
30 }
31
32 //通过MBeanServer来取MXBean的属性或执行操作,都会通过这个方法,然后到真正的Source Object执行相应的方法
33 private Object invokeWithOpenReturn(Object obj, Object[] params)
34 throws MBeanException, IllegalAccessException,
35 InvocationTargetException {
36 final Object[] javaParams;
37 try {
38 //把Open Type类型参数的值转换到Java Type类型的值
39 javaParams = fromOpenParameters(params);
40 } catch (InvalidObjectException e) {
41 // probably can't happen
42 final String msg = methodName() + ": cannot convert parameters " +
43 "from open values: " + e;
44 throw new MBeanException(e, msg);
45 }
46 //通过Source Object执行真正MXBean实例的方法
47 final Object javaReturn = method.invoke(obj, javaParams);
48 try {
49 //把需要返回给调用者的Java Type返回值,转换成Open Type的值。
50 return returnMapping.toOpenValue(javaReturn);
51 } catch (OpenDataException e) {
52 // probably can't happen
53 final String msg = methodName() + ": cannot convert return " +
54 "value to open value: " + e;
55 throw new MBeanException(e, msg);
56 }
57 }
58
59 final Object[] fromOpenParameters(Object[] params)
60 throws InvalidObjectException {
61 if (paramConversionIsIdentity || params == null)
62 return params;
63 final Object[] jparams = new Object[params.length];
64 for (int i = 0; i < params.length; i++)
65 //通过Java Type - Open Type映射关系,实现类型转换
66 jparams[i] = paramMappings[i].fromOpenValue(params[i]);
67 return jparams;
68 }
69
总结:JMX中对于MXBean的实现中可以看出,主要是定义了一套Open Type,使client端不需要知道Server端MXBean里面相关属性类型的情况下,能得到需要的数据,使程序更具更加灵活、两端耦合段更低。为了得到这种便利性,我们自定义的MXBean和里面相关的自定义类型都需要按照一定规范来实现。其实JMX中的OpenMBean就是用这套Open Types使MBean具有"Open"特性的,具体可以参操MXBean的实现。
参考:http://tuhaitao.iteye.com/blog/807398 (JMX学习笔记(三)-MXBean )