Spring中的JMX(Java管理扩展)支持提供了一些特性,使你能够轻松、透明地将Spring应用程序集成到JMX基础设施中。
具体来说,Spring的JMX支持提供了四个核心功能:
- 将任何Spring Bean自动注册为 JMX MBean。
- 控制bean管理接口的灵活机制。
- 通过远程JSR-160连接器声明性地公开MBeans。
- 本地和远程MBean资源的简单代理。
这些特性被设计成在不将应用程序组件耦合到Spring或JMX接口和类的情况下工作。实际上,在大多数情况下,应用程序类不需要知道Spring或JMX,就可以利用spring jmx特性。
一、JMX介绍
JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
JMX架构图

从图中我们可以看到,JMX的结构一共分为三层:
基础层:主要是Mbean,被管理的java bean
Mbean分为如下四种:
- standard MBean:这种类型的MBean最简单,它能管理的资源(包括属性,方法,时间)必须定义在接口中,然后MBean必须实现这个接口。它的命名也必须遵循一定的规范,例如我们的MBean为Hello,则接口必须为HelloMBean。
- dynamic MBean:必须实现javax.management.DynamicMBean接口,所有的属性,方法都在运行时定义。
- model MBean:与标准和动态MBean相比,你可以不用写MBean类,只需使用javax.management.modelmbean.RequiredModelMBean即可。RequiredModelMBean实现了ModelMBean接口,而ModelMBean扩展了DynamicMBean接口,因此与DynamicMBean相似,Model MBean的管理资源也是在运行时定义的。与DynamicMBean不同的是,DynamicMBean管理的资源一般定义在DynamicMBean中(运行时才决定管理那些资源),而model MBean管理的资源并不在MBean中,而是在外部(通常是一个类),只有在运行时,才通过set方法将其加入到model MBean中。
适配层:MbeanServer,提供对资源的注册和管理
接入层: 提供远程访问的入口
standard MBean演示
根据standard MBean的要求,我们首先要定义一个MBean接口,接口的命名规范以具体的实现类为前缀,为了后续可以注册到MBean Server中。
package jmx;
public interface HelloMBean
{
public String getName();
public void setName(String name);
public String getAge();
public void setAge(String age);
public void helloWorld();
public void helloWorld(String str);
public void getTelephone();
}
定义一个实现类
package jmx;
/*
* 该类名称必须与实现的接口的前缀保持一致(即MBean前面的名称
*/
public class Hello implements HelloMBean
{
private String name;
private String age;
public void getTelephone()
{
System.out.println("get Telephone");
}
public void helloWorld()
{
System.out.println("hello world");
}
public void helloWorld(String str)
{
System.out.println("helloWorld:" + str);
}
public String getName()
{
System.out.println("get name 123");
return name;
}
public void setName(String name)
{
System.out.println("set name 123");
this.name = name;
}
public String getAge()
{
System.out.println("get age 123");
return age;
}
public void setAge(String age)
{
System.out.println("set age 123");
this.age = age;
}
}
定义agent层
package jmx;
import java.lang.management.ManagementFactory;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class HelloAgent
{
public static void main(String[] args) throws JMException, Exception
{
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName helloName = new ObjectName("jmxBean:name=hello");
//create mbean and register mbean
server.registerMBean(new Hello(), helloName);
Thread.sleep(60*60*1000);
}
}
- 通过工厂类获取Mbean Server,用来做Mbean的容器
- ObjectName的取名规范:域名:name=Mbean名称,其中域名和Mbean的名称可以任取。这样定义后,我们可以唯一标示我们定义的这个Mbean的实现类了
- 最后将Hello这个类注册到MbeanServer中,注入需要创建一个ObjectName类,我们可以用jdk自带的Jconsole用来观察,可以设置属性值和调用相关方法。
通知(Notification)
MBean之间的通信是必不可少的,Notification起到了在MBean之间沟通桥梁的作用。JMX 的通知由四部分组成:
- Notification这个相当于一个信息包,封装了需要传递的信息
- Notification broadcaster这个相当于一个广播器,把消息广播出。
- Notification listener 这是一个监听器,用于监听广播出来的通知信息。
- Notification filiter 这个一个过滤器,过滤掉不需要的通知。这个一般很少使用。
保留Hello及HelloMBean,增加如下:
package jmx;
public interface JackMBean
{
public void hi();
}
package jmx;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
public class Jack extends NotificationBroadcasterSupport implements JackMBean
{
private int seq = 0;
public void hi()
{
//创建一个信息包
Notification notify =
//通知名称;谁发起的通知;序列号;发起通知时间;发送的消息
new Notification("jack.hi",this,++seq,System.currentTimeMillis(),"jack");
sendNotification(notify);
}
}
这里的类Jack不仅实现了MBean接口,还继承了NotificationBroadcasterSupport。jack在这里创建并发送了一个消息包。
package jmx;
import javax.management.Notification;
import javax.management.NotificationListener;
public class HelloListener implements NotificationListener
{
public void handleNotification(Notification notification, Object handback)
{
if(handback instanceof Hello)
{
Hello hello = (Hello)handback;
hello.printHello(notification.getMessage());
}
}
}
对HelloAgent做以下修改
package jmx;
import java.lang.management.ManagementFactory;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class HelloAgent
{
public static void main(String[] args) throws JMException, Exception
{
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName helloName = new ObjectName("yunge:name=Hello");
Hello hello=new Hello();
server.registerMBean(hello, helloName);
Jack jack = new Jack();
server.registerMBean(jack, new ObjectName("jack:name=Jack"));
jack.addNotificationListener(new HelloListener(), null, hello);
Thread.sleep(500000);
}
}
我们利用jconsole调用jack的hi方法,这里当jack发出消息后,Notification被广播至所有的MBean,当有MBean属于Hello类时则调用Hello的printHello()方法。
JMX的应用
在linux下利用jmx监控Tomcat,在catlina.sh中进行一些环境变零的配置
- Dcom.sun.management.jmxremote=true:相关 JMX 代理侦听开关
- Djava.rmi.server.hostname:服务器端的IP
- Dcom.sun.management.jmxremote.port=29094:相关 JMX 代理侦听请求的端口
- Dcom.sun.management.jmxremote.ssl=false:指定是否使用 SSL 通讯
- Dcom.sun.management.jmxremote.authenticate=false:指定是否需要密码验证
二、将bean导出到JMX
Spring的JMX框架中的核心类是MBeanExporter。这个类负责获取Spring Bean并将它们注册到JMX MBeanServer。例如,考虑以下类:
package org.springframework.jmx;
public class JmxTestBean implements IJmxTestBean {
private String name;
private int age;
private boolean isSuperman;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public int add(int x, int y) {
return x + y;
}
public void dontExposeMe() {
throw new RuntimeException();
}
}
要将此bean的属性和方法公开为MBean的属性和操作,可以在配置文件中配置MBeanExporter类的实例并传入bean,如下例所示:
<beans>
<!-- this bean must not be lazily initialized if the exporting is to happen -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
前面配置片段中的相关bean定义是exporter bean。beans属性告诉MBeanExporter哪些bean必须导出到JMX MBeanServer。在默认配置中,bean映射中每个条目的键被用作相应条目值引用的bean的对象名。你可以更改此行为,如控制bean的ObjectName实例中所述。
使用此配置,testBean bean将作为ObjectName下的MBean公开bean:name=testBean1。默认情况下,bean的所有公共属性都作为属性公开,所有公共方法(继承自对象类的方法除外)作为操作公开。
MBeanExporter是一个生命周期bean。默认情况下,在应用程序生命周期中尽可能晚地导出MBean。你可以通过设置autoStartup标志来配置导出发生的阶段或禁用自动注册。
创建MBeanServer
上一节中显示的配置假定应用程序运行在一个(且只有一个)已运行MBeanServer的环境。在本例中,Spring尝试定位正在运行的MBeanServer,并在该服务器上注册bean(如果有的话)。当你的应用程序在具有自己的MBeanServer的容器(如Tomcat或ibm websphere)中运行时,此行为非常有用。
但是,这种方法在独立环境中或在不提供MBeanServer的容器中运行时没有用处。为了解决这个问题,你可以通过添加org.springframework.jmx.support.MBeanServerFactoryBean类到你的配置。你还可以通过将MBeanExporter实例的server属性的值设置为MBeanServerFactoryBean返回的MBeanServer值来确保使用特定的MBeanServer,如下例所示:
<beans>
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
<!--
this bean needs to be eagerly pre-instantiated in order for the exporting to occur;
this means that it must not be marked as lazily initialized
-->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="server" ref="mbeanServer"/>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
在前面的示例中,MBeanServer的实例由MBeanServerFactoryBean创建,并通过server属性提供给MBeanExporter。当你提供自己的MBeanServer实例时,MBeanExporter不会尝试定位正在运行的MBeanServer,而是使用提供的MBeanServer实例。。要使其正常工作,你的类路径上必须有一个JMX实现。
重用已存在的MBeanServer
如果未指定服务器,则MBeanExporter将尝试自动检测正在运行的MBeanServer。这适用于大多数环境,其中只使用一个MBeanServer实例。但是,当存在多个实例时,导出程序可能会选择错误的服务器。在这种情况下,你应该使用MBeanServer agentId来指示要使用哪个实例,如下例所示:
<beans>
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<!-- indicate to first look for a server -->
<property name="locateExistingServerIfPossible" value="true"/>
<!-- search for the MBeanServer instance with the given agentId -->
<property name="agentId" value="MBeanServer_instance_agentId>"/>
</bean>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="server" ref="mbeanServer"/>
...
</bean>
</beans>
对于现有MBeanServer具有动态的agentId的平台或情况,应使用factory方法,如下例所示:
<beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="server">
<!-- Custom MBeanServerLocator -->
<bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/>
</property>
</bean>
<!-- other beans here -->
</beans>
延迟初始化MBeans
如果你使用MBeanExporter配置bean,该MBeanExporter也配置为延迟初始化,MBeanExporter不会破坏这个契约,并且避免实例化bean。相反,它向MBeanServer注册一个代理,并延迟从容器获取bean,直到对代理进行第一次调用。
MBeans的自动注册
任何通过MBeanExporter导出的、已经是有效MBean的bean都将按原样在MBeanServer中注册,而无需Spring的进一步干预。通过将autodetect属性设置为true,可以使MBeanExporter自动检测到mbean,如下例所示:
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="autodetect" value="true"/>
</bean>
<bean name="spring:mbean=true" class="org.springframework.jmx.export.TestDynamicMBean"/>
控制注册行为
考虑这样一个场景:考虑这样一个场景:Spring MBeanExporter尝试使用ObjectName向MBeanServer注册MBeanbean:name=testBean1。如果已经在同一个ObjectName下注册了一个MBean实例,默认行为是fail(并抛出InstanceAlreadyExistsException)。
你可以精确地控制当MBean注册到MBeanServer时发生的事情。Spring的JMX支持允许三种不同的注册行为来控制注册行为,当注册过程发现一个MBean已经注册在同一个ObjectName下时。下表总结了这些注册行为:
- FAIL_ON_EXISTING:这是默认的注册行为。如果一个MBean实例已经在同一个ObjectName下注册,那么正在注册的MBean不会被注册,并且会抛出InstanceAlreadyExistsException。现有的MBean不受影响。
- IGNORE_EXISTING:如果一个MBean实例已经在同一个ObjectName下注册,那么正在注册的MBean不会被注册。现有的MBean不会受到影响,也不会引发异常。在多个应用程序希望在共享MBeanServer中共享一个公共MBean的设置中,这很有用。
- REPLACE_EXISTING:如果一个MBean实例已经在同一个ObjectName下注册,那么先前注册的现有MBean将被注销,而新的MBean将在其位置上注册(新的MBean实际上替换了以前的实例)。
上述中的值定义为RegistrationPolicy类上的枚举。如果要更改默认注册行为,则需要将MBeanExporter定义上的registrationPolicy属性的值设置为这些值之一。
<beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
<property name="registrationPolicy" value="REPLACE_EXISTING"/>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
三、控制bean的管理接口
时刻与技术进步,每天一点滴,日久一大步!!!
本博客只为记录,用于学习,如有冒犯,请私信于我。