系列文章目录
Java管理扩展JMX入门学习
Spring JMX编程学习(一)- 手动注册bean到MBeanServer
Spring JMX编程学习(二)- 以Bean的方式注册MbeanServer
Spring JMX编程学习(三)- 自定义JMX客户端
Spring JMX编程学习(四)- MBeans自动探测与注解
Spring JMX编程学习(五)- SpringBoot自动注册
文章目录
前言
Java管理扩展(JMX)(Java Management Extensions
)技术是Java平台标准版(Java SE平台)的标准部分。JMX技术已在Java 2 Platform Standard Edition(J2SE)5.0版本中添加到该平台。
JMX技术提供了一种简单、标准的方法来管理资源,例如应用程序(applications),设备(devices)和服务(services)。由于JMX技术是动态的,因此可以在创建、安装和实施资源时使用它来监视和管理资源。您还可以使用JMX技术来监视和管理Java虚拟机(Java VM)。
JMX规范使用Java编程语言定义了体系结构,设计模式,API和服务,用于管理和监视应用程序和网络。
使用JMX技术,给定资源由一个或多个称为“Managed Beans
”或MBean
的Java对象检测。这些MBean在核心管理的对象服务器(称为MBean服务器
)中注册。MBean服务器充当管理代理,并且可以在启用了Java编程语言的大多数设备上运行。
这些规范定义了用于管理已正确配置用于管理的任何资源的JMX代理。 JMX代理由其中注册了MBean的MBean服务器和用于处理MBean的一组服务组成。这样,JMX代理可以直接控制资源,并使资源可用于远程管理应用程序。
检测资源的方式完全独立于管理基础架构。因此,无论如何实现其管理应用程序,都可以使资源变得可管理。
JMX技术定义了标准连接器(称为JMX连接器 JMX connectors
),使您可以从远程管理应用程序访问JMX代理。使用不同协议的JMX连接器提供相同的管理接口。因此,无论使用何种通信协议,管理应用程序都可以透明地管理资源。不符合JMX规范的系统或应用程序也可以使用JMX代理,只要这些系统或应用程序支持JMX代理即可。
提示:以下是本篇文章正文内容,下面案例可供参考
一、标准MBeans
1. 创建MBean接口
package com.example.jmx;
/**
* By convention, an MBean interface takes the name of the Java class that implements it,
* with the suffix MBean added
*/
public interface HelloMBean {
// declares two operations: the Java methods add() and sayHello()
void sayHello();
int add(int x, int y);
// two attributes: Name is a read-only string, and CacheSize is an integer that can be both read and written
String getName();
int getCacheSize();
void setCacheSize(int size);
}
2. MBean实现类
package com.example.jmx;
public class Hello implements HelloMBean {
private static final int
DEFAULT_CACHE_SIZE = 200;
private final String name = "Reginald";
private int cacheSize = DEFAULT_CACHE_SIZE;
@Override
public void sayHello() {
System.out.println("hello, world");
}
@Override
public int add(int x, int y) {
return x + y;
}
@Override
public String getName() {
return this.name;
}
@Override
public int getCacheSize() {
return this.cacheSize;
}
@Override
public synchronized void setCacheSize(int size) {
this.cacheSize = size;
System.out.println("Cache size now " + this.cacheSize);
}
}
3. 创建一个JMX代理用于管理资源
package com.example.jmx;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
public class Main {
public static void main(String[] args) throws Exception {
// obtaining an MBean server created and initialized by the platform
// If no MBean server has been created by the platform already, then getPlatformMBeanServer() creates an MBean
// server automatically by calling the JMX method MBeanServerFactory.createMBeanServer()
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
// 每个Mbean都必须包含ObjectName
ObjectName name = new ObjectName("com.example.jmx:type=Hello");
Hello mbean = new Hello();
mbs.registerMBean(mbean, name);
System.out.println("Waiting forever...");
Thread.sleep(Long.MAX_VALUE);
}
}
在以上的main方法当中,通过java.lang.management.ManagementFactory#getPlatformMBeanServer
方法获取一个已经创建和初始化的MBean server
,如果当前平台还没有创建的话,该方法会自动调用javax.management.MBeanServerFactory#createMBeanServer
方法创建一个。并同时注册一些JDK中自带的MXBeans,比如java.lang.management.MemoryPoolMXBean
和java.lang.management.MemoryManagerMXBean
.创建MBeanServer
之后,定义了一个MBean实例,每个MBean实例必须包含一个ObjectName
,这个对象的值必须按照JMX的标准来定义,比如上面的com.example.jmx:type=Hello
就包含包名、双引号、type、等号和MBean的名称。然后创建一个mbean
对象,并通过javax.management.MBeanServer#registerMBean
方法注册到MBeanServer
当中。
private static MBeanServer platformMBeanServer;
public static synchronized MBeanServer getPlatformMBeanServer() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
Permission perm = new MBeanServerPermission("createMBeanServer");
sm.checkPermission(perm);
}
// 如果platformMBeanServer还不存在 就会创建一个
if (platformMBeanServer == null) {
platformMBeanServer = MBeanServerFactory.createMBeanServer();
// 自动注册一些平台的MXBeans 比如MemoryPoolMXBean和MemoryManagerMXBean
for (PlatformComponent pc : PlatformComponent.values()) {
List<? extends PlatformManagedObject> list =
pc.getMXBeans(pc.getMXBeanInterface());
for (PlatformManagedObject o : list) {
// Each PlatformComponent represents one management
// interface. Some MXBean may extend another one.
// The MXBean instances for one platform component
// (returned by pc.getMXBeans()) might be also
// the MXBean instances for another platform component.
// e.g. com.sun.management.GarbageCollectorMXBean
//
// So need to check if an MXBean instance is registered
// before registering into the platform MBeanServer
if (!platformMBeanServer.isRegistered(o.getObjectName())) {
addMXBean(platformMBeanServer, o);
}
}
}
HashMap<ObjectName, DynamicMBean> dynmbeans =
ManagementFactoryHelper.getPlatformDynamicMBeans();
for (Map.Entry<ObjectName, DynamicMBean> e : dynmbeans.entrySet()) {
addDynamicMBean(platformMBeanServer, e.getValue(), e.getKey());
}
for (final PlatformManagedObject o :
ExtendedPlatformComponent.getMXBeans()) {
if (!platformMBeanServer.isRegistered(o.getObjectName())) {
addMXBean(platformMBeanServer, o);
}
}
}
// 已经存在 则直接返回
return platformMBeanServer;
}
4. JConsole作为客户端管理
在jdk的bin目录下查找到jconsole小工具,然后打开
选择到自己的进程,并点击下面的连接,连接过程提示安全问题,可以忽略,选用不安全的连接
.
这个jConsol也称为监视和管理控制台,工具栏中包含有一些内存
、线程
、类
这些信息,其实都是通过JMX技术从服务端获取的(系统默认注册)。我们现在看mBean
这个选项。很容易就会发现我们自己注册的mBean(com.example.jmx.Hello
)。在mBeanInfo
当中我们可以看到ObjectName
和ClassName
信息,另外在描述符里面也有interfaceClassName
这些信息。
在左侧我们可以看到这个mBean的属性和操作,其实在定义mBean接口的时候,我们通过setter/getter方法来定义属性(Attributes
),如果只有getter方法,这个属性就是只读的,包含setter/getter方法才是可写可读,如果不是setter/getter方法那么就属于操作(Operations
)。比如上面的add和sayHello。
对于属性cacheSize,就是可读可写的,当前的值为200.
尝试修改这个属性,被监视和管理的程序内存值就被改变了。
Name这个属性是不可写入的,因为com.example.jmx.HelloMBean
接口中没有提供setter方法。
接下来我们再看一下操作,通过jConsole来执行一下操作。
5. 通知
JMX API定义了一种机制,使MBean能够生成通知,例如,以信号通知状态更改,检测到的事件或问题。
要生成通知,MBean必须实现接口NotificationEmitter或扩展NotificationBroadcasterSupport。 要发送通知,您需要构造类javax.management.Notification的实例或子类(例如AttributeChangedNotification),然后将该实例传递给NotificationBroadcasterSupport.sendNotification。
每个通知都有一个来源。 源是生成通知的MBean的对象名称。
每个通知都有一个序列号。 当订单很重要时,此数字可用于订购来自同一来源的通知,并且存在以错误的顺序处理通知的风险。 序列号可以为零,但是对于给定MBean的每个通知,序列号最好递增。
修改MBean定义
package com.example.jmx;
import javax.management.AttributeChangeNotification;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
public class Hello
extends NotificationBroadcasterSupport implements HelloMBean {
private static final int DEFAULT_CACHE_SIZE = 200;
private final String name = "Reginald";
private int cacheSize = DEFAULT_CACHE_SIZE;
private long sequenceNumber = 1;
@Override
public void sayHello() {
System.out.println("hello, world");
}
@Override
public int add(int x, int y) {
return x + y;
}
/* Getter for the Name attribute. The pattern shown here is frequent: the
getter returns a private field representing the attribute value. In our
case, the attribute value never changes, but for other attributes it
might change as the application runs. Consider an attribute representing
statistics such as uptime or memory usage, for example. Being read-only
just means that it can't be changed through the management interface. */
@Override
public String getName() {
return this.name;
}
/* Getter for the CacheSize attribute. The pattern shown here is
frequent: the getter returns a private field representing the
attribute value, and the setter changes that field. */
@Override
public int getCacheSize() {
return this.cacheSize;
}
/* Setter for the CacheSize attribute. To avoid problems with
stale values in multithreaded situations, it is a good idea
for setters to be synchronized. */
@Override
public synchronized void setCacheSize(int size) {
int oldSize = this.cacheSize;
this.cacheSize = size;
/* In a real application, changing the attribute would
typically have effects beyond just modifying the cacheSize
field. For example, resizing the cache might mean
discarding entries or allocating new ones. The logic for
these effects would be here. */
System.out.println("Cache size now " + this.cacheSize);
/* Construct a notification that describes the change. The
"source" of a notification is the ObjectName of the MBean
that emitted it. But an MBean can put a reference to
itself ("this") in the source, and the MBean server will
replace this with the ObjectName before sending the
notification on to its clients.
For good measure, we maintain a sequence number for each
notification emitted by this MBean.
The oldValue and newValue parameters to the constructor are
of type Object, so we are relying on Tiger's autoboxing
here. */
Notification n =
new AttributeChangeNotification(this,
sequenceNumber++,
System.currentTimeMillis(),
"CacheSize changed",
"CacheSize",
"int",
oldSize,
this.cacheSize);
/* Now send the notification using the sendNotification method
inherited from the parent class NotificationBroadcasterSupport. */
sendNotification(n);
}
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
String[] types = new String[]{
AttributeChangeNotification.ATTRIBUTE_CHANGE
};
String name = AttributeChangeNotification.class.getName();
String description = "An attribute of this MBean has changed";
MBeanNotificationInfo info =
new MBeanNotificationInfo(types, name, description);
return new MBeanNotificationInfo[]{info};
}
}
以上AttributeChangeNotification参数:
重启通过JConsole连接
点击订阅
在com.example.jmx.Hello#setCacheSize方法中会发送通知(sendNotification),因为修改属性
通过以上的小案例,我们展示了MBeans的使用。总结一下:
MBean是类似于JavaBeans组件的托管Java对象,它遵循JMX规范中阐述的设计模式。 MBean可以代表设备,应用程序或任何需要管理的资源。 MBean公开了一个包含以下内容的管理接口:
- 一组可读或可写的属性,或两者兼而有之。
- 一组可调用的操作。
- 自我描述。
在MBean实例的整个生命周期中,管理界面都不会更改。 当某些预定义事件发生时,MBeans也可以发出通知。
二、MXBeans
MXBean是一种MBean,仅引用一组预定义的数据类型。 这样,您可以确保您的MBean可被任何客户端(包括远程客户端)使用,而无需客户端有权访问代表MBeans类型的特定于模型的类。 MXBean提供了一种方便的方式将相关值捆绑在一起,而无需将客户端进行特殊配置以处理捆绑。
以与标准MBean相同的方式,通过编写称为SomethingMXBean的Java接口和实现该接口的Java类来定义MXBean。 但是,与标准MBean不同,MXBean不需要将Java类称为Something。 接口中的每个方法都定义MXBean中的属性或操作。 注释@MXBean也可以用于注释Java接口,而不是要求接口名称后跟MXBean后缀。
1. 自定义数据类型
创建一个自定义的数据类型:
/*
* QueueSample.java - Java type representing a snapshot of a given queue.
* It bundles together the instant time the snapshot was taken, the queue
* size and the queue head.
*/
package com.example.mxb;
import java.beans.ConstructorProperties;
import java.util.Date;
public class QueueSample {
private final Date date;
private final int size;
private final String head;
/**
* convert the given instance into a CompositeData instance and uses the @ConstructorProperties annotation to
* reconstruct a QueueSample instance from a CompositeData instance
*/
@ConstructorProperties({"date", "size", "head"})
public QueueSample(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;
}
}
2. 创建MXBean接口
创建一个MXBean接口:
package com.example.mxb;
public interface QueueMXBean {
public QueueSample getQueueSample();
public void clearQueue();
}
3. 创建MXBean实例
创建一个MXBean实例,注意这里的实例不需要名称为Queue(接口名称除去MXBean部分
),可以是任意其他名字,此处使用QueueSampler
package com.example.mxb;
import java.util.Date;
import java.util.Queue;
public class QueueSampler implements QueueMXBean {
private Queue<String> queue;
public QueueSampler(Queue<String> queue) {
this.queue = queue;
}
@Override
public QueueSample getQueueSample() {
synchronized (queue) {
return new QueueSample(new Date(), queue.size(), queue.peek());
}
}
@Override
public void clearQueue() {
synchronized (queue) {
queue.clear();
}
}
}
4. 创建JMX代理管理资源
代码如下(与MBean注册方式是一模一样的):
package com.example.mxb;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
public class Main {
/* For simplicity, we declare "throws Exception".
Real programs will usually want finer-grained exception handling. */
public static void main(String[] args) throws Exception {
// Get the Platform MBean Server
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
// Construct the ObjectName for the QueueSampler MXBean we will register
ObjectName mxbeanName = new ObjectName("com.example.mxb:type=QueueSampler");
// Create the Queue Sampler MXBean
Queue<String> queue = new ArrayBlockingQueue<String>(10);
queue.add("Request-1");
queue.add("Request-2");
queue.add("Request-3");
QueueSampler mxbean = new QueueSampler(queue);
// Register the Queue Sampler MXBean
mbs.registerMBean(mxbean, mxbeanName);
// Wait forever
System.out.println("Waiting for incoming requests...");
Thread.sleep(Long.MAX_VALUE);
}
}
运行main程序,通过Jconsole查看
- 查看属性
可以点击刷新按钮获取最新的值
- 查看操作
原来队列中是有三个元素的,在清空队列之后,再去查看
总结一下,MXBean的操作和MBean其实是差不多的,就是在规范方面不同而已,比如实例名称与接口不需要硬性符合某种关系,另外可以使用自己定义的数据接口传输属性。
三、远程连接与自定义JMX客户端
1. 通过端口暴露程序用于远程管理
JMX技术允许我们通过JMX connectors
进行远程管理,在上面的几个案例当中其实JConsole和被管理的程序使用的其实是一个JVM实例。如果是远程管理,应该是属于不同的JVM实例。
JMX连接器使MBean服务器可供基于Java技术的远程客户端访问。连接器的客户端导出与MBean服务器基本相同的接口。
JMX连接器由连接器客户端和连接器服务器组成。连接器服务器连接到MBean服务器,并侦听来自客户端的连接请求。连接器客户端负责与连接器服务器建立连接。连接器客户端通常与连接器服务器位于不同的Java虚拟机(Java VM)中,并且通常在不同的计算机上运行。 JMX API定义了基于远程方法调用(RMI)的标准连接协议。使用此协议,您可以将JMX客户端从远程位置连接到MBean服务器中的MBean,并在MBean上执行操作,就像在本地执行操作一样。
Java SE平台提供了一种开箱即用的方法,可以使用JMX API的标准RMI连接器来远程监视应用程序。开箱即用的RMI连接器自动公开用于远程管理的应用程序,而无需您自己创建专用的远程连接器服务器。通过使用正确的属性启动Java应用程序来激活即用型远程管理代理。然后,与JMX技术兼容的监视和管理应用程序可以连接到这些应用程序并进行远程监视。
首先将以上两个小程序合并到一起,使用一个主程序启动
/*
* Main.java - main class for the Hello MBean and QueueSampler MXBean example.
* Create the Hello MBean and QueueSampler MXBean, register them in the platform
* MBean server, then wait forever (or until the program is interrupted).
*/
package com.example.notify;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
public class Main {
/* For simplicity, we declare "throws Exception".
Real programs will usually want finer-grained exception handling. */
public static void main(String[] args) throws Exception {
// Get the Platform MBean Server
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
// Construct the ObjectName for the Hello MBean we will register
ObjectName mbeanName = new ObjectName("com.example.notify:type=Hello");
// Create the Hello World MBean
Hello mbean = new Hello();
// Register the Hello World MBean
mbs.registerMBean(mbean, mbeanName);
// Construct the ObjectName for the QueueSampler MXBean we will register
ObjectName mxbeanName = new ObjectName("com.example.notify:type=QueueSampler");
// Create the Queue Sampler MXBean
Queue<String> queue = new ArrayBlockingQueue<String>(10);
queue.add("Request-1");
queue.add("Request-2");
queue.add("Request-3");
QueueSampler mxbean = new QueueSampler(queue);
// Register the Queue Sampler MXBean
mbs.registerMBean(mxbean, mxbeanName);
// Wait forever
System.out.println("Waiting for incoming requests...");
Thread.sleep(Long.MAX_VALUE);
}
}
并通过以下方式运行主程序(模拟远程端口为9999)
java -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false com.example.notify.Main
注意需要在项目根目录运行
当然也可以通过IDE设置系统参数的方式(比如IDEA当中)
启动完成之后,通过JConsole远程连接
2. 自定义JMX客户端管理程序
本教程的先前课程向您展示了如何创建JMX技术MBean和MXBean,以及如何向JMX代理注册它们。 但是,所有前面的示例都使用了现有的JMX客户端JConsole。 本课将演示如何创建自己的自定义JMX客户端。
package com.example.notify;
import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
public class Client {
/* For simplicity, we declare "throws Exception".
Real programs will usually want finer-grained exception handling. */
public static void main(String[] args) throws Exception {
// Create an RMI connector client and
// connect it to the RMI connector server
//
首先通过javax.management.remote.JMXConnectorFactory#connect
工厂方法获取一个JMXConnector
对象。创建这个对象主要是用于创建一个MBeanServerConnection
.
echo("\nCreate an RMI connector client and " +
"connect it to the RMI connector server");
// 前提是开启MBeanXServer java -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false com.example.notify.Main
JMXServiceURL url =
new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
// Get an MBeanServerConnection
// 获取连接
echo("\nGet an MBeanServerConnection");
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
waitForEnterPressed();
获取连接之后,就可以获取到已经注册的MBean了,方法为javax.management.MBeanServerConnection#getDomains
。还有其他的一些有用接口getDefaultDomain、getMBeanCount等。
// Get domains from MBeanServer
//
echo("\nDomains:");
String domains[] = mbsc.getDomains();
Arrays.sort(domains);
for (String domain : domains) {
echo("\tDomain = " + domain);
}
waitForEnterPressed();
// Get MBeanServer's default domain
//
echo("\nMBeanServer default domain = " + mbsc.getDefaultDomain());
// Get MBean count
//
echo("\nMBean count = " + mbsc.getMBeanCount());
// Query MBean names
//
echo("\nQuery MBeanServer MBeans:");
Set<ObjectName> names =
new TreeSet<ObjectName>(mbsc.queryNames(null, null));
for (ObjectName name : names) {
echo("\tObjectName = " + name);
}
waitForEnterPressed();
以上代码运行结果如下
Create an RMI connector client and connect it to the RMI connector server
Get an MBeanServerConnection
Press <Enter> to continue...
Domains:
Domain = JMImplementation
Domain = com.example.notify
Domain = com.sun.management
Domain = java.lang
Domain = java.nio
Domain = java.util.logging
Press <Enter> to continue...
MBeanServer default domain = DefaultDomain
MBean count = 24
Query MBeanServer MBeans:
ObjectName = JMImplementation:type=MBeanServerDelegate
ObjectName = com.example.notify:type=Hello
ObjectName = com.example.notify:type=QueueSampler
ObjectName = com.sun.management:type=DiagnosticCommand
ObjectName = com.sun.management:type=HotSpotDiagnostic
ObjectName = java.lang:type=ClassLoading
ObjectName = java.lang:type=Compilation
ObjectName = java.lang:type=GarbageCollector,name=PS MarkSweep
ObjectName = java.lang:type=GarbageCollector,name=PS Scavenge
ObjectName = java.lang:type=Memory
ObjectName = java.lang:type=MemoryManager,name=CodeCacheManager
ObjectName = java.lang:type=MemoryManager,name=Metaspace Manager
ObjectName = java.lang:type=MemoryPool,name=Code Cache
ObjectName = java.lang:type=MemoryPool,name=Compressed Class Space
ObjectName = java.lang:type=MemoryPool,name=Metaspace
ObjectName = java.lang:type=MemoryPool,name=PS Eden Space
ObjectName = java.lang:type=MemoryPool,name=PS Old Gen
ObjectName = java.lang:type=MemoryPool,name=PS Survivor Space
ObjectName = java.lang:type=OperatingSystem
ObjectName = java.lang:type=Runtime
ObjectName = java.lang:type=Threading
ObjectName = java.nio:type=BufferPool,name=direct
ObjectName = java.nio:type=BufferPool,name=mapped
ObjectName = java.util.logging:type=Logging
Press <Enter> to continue...
其中就包含我们创建的
Domain = com.example.notify和ObjectName = com.example.notify:type=Hello、ObjectName = com.example.notify:type=QueueSampler,接下来我们在客户端通过创建Mbean的代理(javax.management.JMX#newMBeanProxy(javax.management.MBeanServerConnection, javax.management.ObjectName, java.lang.Class<T>, boolean)
)、监听然后修改属性。
// ----------------------
// Manage the Hello MBean
// ----------------------
echo("\n>>> Perform operations on Hello MBean <<<");
// Construct the ObjectName for the Hello MBean
//
ObjectName mbeanName = new ObjectName("com.example.notify:type=Hello");
// Create a dedicated proxy for the MBean instead of
// going directly through the MBean server connection
// 创建一个远程代理
HelloMBean mbeanProxy =
JMX.newMBeanProxy(mbsc, mbeanName, HelloMBean.class, true);
// Add notification listener on Hello MBean
//
echo("\nAdd notification listener...");
// Create listener
// 创建一个监听器 所谓的订阅
ClientListener listener = new ClientListener();
mbsc.addNotificationListener(mbeanName, listener, null, null);
// Get CacheSize attribute in Hello MBean
// 获取值
echo("\nCacheSize = " + mbeanProxy.getCacheSize());
// Set CacheSize attribute in Hello MBean
// Calling "reset" makes the Hello MBean emit a
// notification that will be received by the registered
// ClientListener.
// 设置值
mbeanProxy.setCacheSize(150);
// Sleep for 2 seconds to have time to receive the notification
//
echo("\nWaiting for notification...");
sleep(2000);
// Get CacheSize attribute in Hello MBean
//
echo("\nCacheSize = " + mbeanProxy.getCacheSize());
// Invoke "sayHello" in Hello MBean
//
echo("\nInvoke sayHello() in Hello MBean...");
mbeanProxy.sayHello();
// Invoke "add" in Hello MBean
//
echo("\nInvoke add(2, 3) in Hello MBean...");
echo("\nadd(2, 3) = " + mbeanProxy.add(2, 3));
waitForEnterPressed();
// ------------------------------
// Manage the QueueSampler MXBean
// ------------------------------
echo("\n>>> Perform operations on QueueSampler MXBean <<<");
// Construct the ObjectName for the QueueSampler MXBean
//
ObjectName mxbeanName =
new ObjectName("com.example.notify:type=QueueSampler");
// Create a dedicated proxy for the MXBean instead of
// going directly through the MBean server connection
//
QueueSamplerMXBean mxbeanProxy =
JMX.newMXBeanProxy(mbsc, mxbeanName, QueueSamplerMXBean.class);
// Get QueueSample attribute in QueueSampler MXBean
//
QueueSample queue1 = mxbeanProxy.getQueueSample();
echo("\nQueueSample.Date = " + queue1.getDate());
echo("QueueSample.Head = " + queue1.getHead());
echo("QueueSample.Size = " + queue1.getSize());
// Invoke "clearQueue" in QueueSampler MXBean
//
echo("\nInvoke clearQueue() in QueueSampler MXBean...");
mxbeanProxy.clearQueue();
// Get QueueSample attribute in QueueSampler MXBean
//
QueueSample queue2 = mxbeanProxy.getQueueSample();
echo("\nQueueSample.Date = " + queue2.getDate());
echo("QueueSample.Head = " + queue2.getHead());
echo("QueueSample.Size = " + queue2.getSize());
waitForEnterPressed();
// Close MBeanServer connection
// 关闭连接
echo("\nClose the connection to the server");
jmxc.close();
echo("\nBye! Bye!");
}
private static void echo(String msg) {
System.out.println(msg);
}
private static void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void waitForEnterPressed() {
try {
echo("\nPress <Enter> to continue...");
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Inner class that will handle the notifications.
*/
public static class ClientListener implements NotificationListener {
@Override
public void handleNotification(Notification notification,
Object handback) {
echo("\nReceived notification:");
echo("\tClassName: " + notification.getClass().getName());
echo("\tSource: " + notification.getSource());
echo("\tType: " + notification.getType());
echo("\tMessage: " + notification.getMessage());
if (notification instanceof AttributeChangeNotification) {
AttributeChangeNotification acn =
(AttributeChangeNotification) notification;
echo("\tAttributeName: " + acn.getAttributeName());
echo("\tAttributeType: " + acn.getAttributeType());
echo("\tNewValue: " + acn.getNewValue());
echo("\tOldValue: " + acn.getOldValue());
}
}
}
}
执行结果如下
>>> Perform operations on Hello MBean <<<
Add notification listener...
CacheSize = 200
Waiting for notification...
Received notification:
ClassName: javax.management.AttributeChangeNotification
Source: com.example.notify:type=Hello
Type: jmx.attribute.change
Message: CacheSize changed
AttributeName: CacheSize
AttributeType: int
NewValue: 150
OldValue: 200
CacheSize = 150
Invoke sayHello() in Hello MBean...
Invoke add(2, 3) in Hello MBean...
add(2, 3) = 5
Press <Enter> to continue...
>>> Perform operations on QueueSampler MXBean <<<
QueueSample.Date = Thu Nov 19 14:34:02 CST 2020
QueueSample.Head = Request-1
QueueSample.Size = 3
Invoke clearQueue() in QueueSampler MXBean...
QueueSample.Date = Thu Nov 19 14:34:02 CST 2020
QueueSample.Head = null
QueueSample.Size = 0
Press <Enter> to continue...
Close the connection to the server
Bye! Bye!
Process finished with exit code 0
总结
以上案例来自官方教程实例:https://docs.oracle.com/javase/tutorial/jmx/TOC.html