之前讲了在使用标准的MBean时,客户端代码中需要包含接口,通过JMX生成这个接口的proxy实例,来调用。
但是,对于MXBeans我们不需要知道被调用的类型。还是直接通过例子来看。
首先还是要定义一个接口,接口的名字要以MXbean结尾。
- package com.example;
- public interface QueueSamplerMXBean {
- public Sample getSample();
- public void clearQueue();
- }
然后就是他的实现:
- package com.example.mxbeans;
- import java.util.Date;
- import java.util.Queue;
- public class QueueSampler implements QueueSamplerMXBean {
- private Queue<String> queue;
- public QueueSampler(Queue<String> queue) {
- this.queue = queue;
- }
- public Sample getSample() {
- synchronized (queue) {
- return new Sample(new Date(), queue.size(), queue.peek());
- }
- }
- public void clearQueue() {
- synchronized (queue) {
- queue.clear();
- }
- }
- }
跟标准MBean不同,MXbean的实现类的类名不需要跟接口对应,比如上面的实现,类名可以改成叫MyQueueSampler等。
这个实现类实现了接口里面定义的两个方法。
下面就是存放在上面的Queue里面的对象类,这其实就是一个java bean:
- public class Sample {
- private final Date date;
- private final int size;
- private final String head;
- @ConstructorProperties({"date", "size", "head"})
- public Sample(Date date, int size, String head) {
- this.date = date;
- this.size = size;
- this.head = head;
- }
- public Date getDate() {
- return date;
- }
- public int getSize() {
- return size;
- }
- public String getHead() {
- return head;
- }
- }
在这个bean里面,构造函数上有一个annotation,@ConstructorProperties,我们一会再解释。
然后就是注册这个MXBean的类,这个类其实就跟标准MBean的注册一样的,
- package com.example.mxbeans;
- import java.lang.management.ManagementFactory;
- import java.util.Queue;
- import java.util.concurrent.ArrayBlockingQueue;
- import javax.management.MBeanServer;
- import javax.management.ObjectName;
- public class Main {
- public static void main(String[] args) throws Exception {
- MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
- ObjectName name =
- new ObjectName("com.example.mxbeans:type=QueueSampler");
- Queue<String> queue = new ArrayBlockingQueue<String>(10);
- queue.add("Request-1");
- queue.add("Request-2");
- queue.add("Request-3");
- QueueSampler mxbean = new QueueSampler(queue);
- mbs.registerMBean(mxbean, name);
- System.out.println("Waiting...");
- Thread.sleep(Long.MAX_VALUE);
- }
- }
在这里,在队列中加了3个数据,用这个队列创建了一个MBean对象,并把它注册到MBean server里。
然后我们运行这个程序:
java com.example.mxbeans.Main
然后打开jconsole,连接到刚才的Main进程。打开刚才注册的MBean对象,查看他的属性Sample:
我们可以看到Sample的值是CompositeDataSupport,而不是我们定义的Sample类的类名。这是因为,在MXbean里,MBean里面使用的数据类型,都会被转换成CompositeData类型,他是一个接口,CompositeDataSupport是他的实现,对于大多数普通对象,都可以被直接转换成CompositeDataSupport类型。就好像一个Map类型,只要里面的数据都是String,int这样的类型,就能转换成一个字符串,同样的道理,在MXBean里,所有的对象,都是被转换成CompositeDataSupport,来发送到客户端的。
我们在Sample类的构造函数里面,使用了@ConstructorProperties({"date", "size", "head"}),其实就是告诉JMX在创建Sample对象的时候,需要几个参数,分别叫什么。
然后在JConsole中,我们双击CompositeDataSupport,就可以看到Sample对象里面的3个成员。
因为在QueueSamplerMXBean里面,只定义了getSample方法,所以只能看,你们可以试着加一个add方法,在调用 的时候,就能看到JMX会为我们提供创建Sample对象时需要的3个成员的输入框。
如果是标准MBean,在Sample的值那一列,就无法显示正确的类型,更无法查看对象,,因为jconsole根本找不到sample类。除非你在启动JConsole的时候,把Sample类所在的目录作为classpath。
通过使用JConsole,我们就能看出MXBean和普通bean的区别。
最后,再看看在客户端使用时的区别:
- import java.util.Date;
- import javax.management.MBeanServerConnection;
- import javax.management.ObjectName;
- import javax.management.openmbean.CompositeData;
- import javax.management.remote.JMXConnector;
- import javax.management.remote.JMXConnectorFactory;
- import javax.management.remote.JMXServiceURL;
- public class SamplerMXBClient {
- public static void main(String[] args) {
- try {
- JMXServiceURL url = new JMXServiceURL(
- "service:jmx:rmi:///jndi/rmi://localhost:8888/jmxrmi");
- JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
- MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
- ObjectName name = new ObjectName("com.example.mxbeans:type=QueueSampler");
- CompositeData sampleComp = (CompositeData)mbsc.getAttribute(name, "Sample");
- Date sampleDate = (Date) sampleComp.get("date");
- System.out.println("sampleDate = " + sampleDate);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
在这个代码里,我们没有使用QueueSampler来得到MBean对象的引用,而且,对这个对象里面Sample属性,也是使用CompositeData来获得,然后,再得到里面的date属性。
从这个代码就可以看出来,对于MXBean类型的MBean使用,我们完全不需要知道对方的类,参数类型,就可以通过CompositeData来得到其数据。
但是,对于稍微复杂的数据,如果都要这样去取,那是一件很麻烦的事情,所以,我们还是可以用类似标准MBean的方式,来得到:
- QueueSamplerMXBean proxy =
- JMX.newMXBeanProxy(mbsc, name, QueueSamplerMXBean.class);
- Sample sample = proxy.getSample();
- Date sampleDate2 = sample.getDate();
- System.out.println("sampleDate2 = " + sampleDate2);
这跟标准MBean的区别就是JMX.newMXBeanProxy()。
那这样的话,标准MBean和MXBean不就没有区别了吗?都是要在客户端使用对象接口来获得proxy对象,调用的时候需要的类也需要,这些都要加到客户端的类路径里面。如果这样使用,确实在使用上,两种类型感觉没什么区别。只是在JMX内部,对于MXBean类型,对象是先转换成CompositeData,然后通过网络传输到客户端,客户端再把它转换成自己的bean类型。这些转换都是自动的,根据bean里面的@ConstructorProperties({"date", "size", "head"})转换。
所以说,MXbeans为用户提供了在无法得到被调用的接口类型和对象类时,只要我们知道它提供的属性数据的结构,也可以得到里面的数据的功能。而且,MXbean提供了非常多的MXBean,例如用于JVM监控的。在做系统监程序的时候,非常有用。