系列文章目录
Java管理扩展JMX入门学习
Spring JMX编程学习(一)- 手动注册bean到MBeanServer
Spring JMX编程学习(二)- 以Bean的方式注册MbeanServer
Spring JMX编程学习(三)- 自定义JMX客户端
Spring JMX编程学习(四)- MBeans自动探测与注解
Spring JMX编程学习(五)- SpringBoot自动注册
前言
在上一章当中我们通过Spring中的org.springframework.jmx.export.MBeanExporter
类将特定的Bean对象注册到MBeanServer中,通过源码分析,其实主要有以下两步,第一步是在MBeanExporter初始化的时候检查MBeanServer是否已经存在,不存在则创建一个,第二部是在所有非懒加载Bean初始化之后根据配置信息注册MBean到MBeanServer当中。其实在Spring当中也提供了其他配置MBeanServer的方式,本章我们就使用使用Bean配置MBeanServer的方式
提示:以下是本篇文章正文内容,依赖上一章的案例
一、添加MBeanServer配置
1. 注册mbeanServer
在spring-jmx-server.xml文件中添加mbeanServer并同时修改exporter并添加server属性引用。
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="beans">
<map>
<entry key="com.example.spring.jmx:name=testBean" value-ref="testBean"/>
</map>
</property>
<property name="server" ref="mbeanServer"/>
</bean>
启动主类
但是通过JConsole无法查看到用户自定义的MBean信息
2. JConsole无法查看问题
使用了MBeanServerFactoryBean注册了之后,程序没有报错,但是通过JConsole却无法查看到用户自定义的MBean信息,这是怎么回事呢?分析一下MBeanServerFactoryBean的源码。
可以看到这个类继承了InitializingBean和FactoryBean两个接口,所以重点在afterPropertiesSet和getObject方法了。其中getObject方法只是简单返回server属性,重点就在afterPropertiesSet方法中,而这个方法就是给server赋值的。
private boolean locateExistingServerIfPossible = false;
@Nullable
private String agentId;
@Override
@Nullable
public MBeanServer getObject() {
return this.server;
}
/**
* Creates the {@code MBeanServer} instance.
*/
@Override
public void afterPropertiesSet() throws MBeanServerNotFoundException {
// Try to locate existing MBeanServer, if desired.
1. 是否使用已经存在的MBeanServer
if (this.locateExistingServerIfPossible || this.agentId != null) {
try {
this.server = locateMBeanServer(this.agentId);
}
catch (MBeanServerNotFoundException ex) {
// If agentId was specified, we were only supposed to locate that
// specific MBeanServer; so let's bail if we can't find it.
if (this.agentId != null) {
throw ex;
}
logger.debug("No existing MBeanServer found - creating new one");
}
}
2. server不存在则创建一个
// Create a new MBeanServer and register it, if desired.
if (this.server == null) {
this.server = createMBeanServer(this.defaultDomain, this.registerWithFactory);
this.newlyRegistered = this.registerWithFactory;
}
}
在afterPropertiesSet的主要逻辑分为两个部分,第一个是当locateExistingServerIfPossible属性设置为true或者agentId不为空的时候(默认情况下这两个条件都不满足);第二部分就是从第一部分没有获得server,此时调用createMBeanServer方法创建一个(传入的参数默认情况下分别为空和true)。
@Nullable
private String defaultDomain;
private boolean registerWithFactory = true;
/**
* Create a new {@code MBeanServer} instance and register it with the
* {@code MBeanServerFactory}, if desired.
* @param defaultDomain the default domain, or {@code null} if none
* @param registerWithFactory whether to register the {@code MBeanServer}
* with the {@code MBeanServerFactory}
* @see javax.management.MBeanServerFactory#createMBeanServer
* @see javax.management.MBeanServerFactory#newMBeanServer
*/
protected MBeanServer createMBeanServer(@Nullable String defaultDomain, boolean registerWithFactory) {
if (registerWithFactory) {
return MBeanServerFactory.createMBeanServer(defaultDomain);
}
else {
return MBeanServerFactory.newMBeanServer(defaultDomain);
}
}
因此此时是通过MBeanServerFactory的createMBeanServer方法创建MBeanServer对象的。而我们之前使用的是ManagementFactory的getPlatformMBeanServer方法获取的,这两个方法有什么区别吗?
前者对应源码如下,通过源码不难发现,这个时候是不论之前是否存在MBeanServer实例,直接创建一个,而getPlatformMBeanServer方法则是如果不存在则创建(同样调用的是以下的createMBeanServer方法),存在的话则使用之前的实例,也就是说使用getPlatformMBeanServer一方面是存在重用的好处(单例),第二个还可以和Java平台公用MBeanServer。因为在我们引入的包中已经存在了不少的MBean,加载的过程中也会创建MBeanServer实例(我们上面JConsle可以查看的MBean就属于这一类了)。
private static final ArrayList<MBeanServer> mBeanServerList =
new ArrayList<MBeanServer>();
public static MBeanServer createMBeanServer() {
return createMBeanServer(null);
}
public static MBeanServer createMBeanServer(String domain) {
checkPermission("createMBeanServer");
// 创建一个新的MBeanServer实例
final MBeanServer mBeanServer = newMBeanServer(domain);
// 添加到MBeanServer实例列表当中
addMBeanServer(mBeanServer);
return mBeanServer;
}
private static synchronized void addMBeanServer(MBeanServer mbs) {
mBeanServerList.add(mbs);
}
根据以上分析的结果,我们再次查看源码,在上面我们提到了如果locateExistingServerIfPossible属性设置为true
的时候,就会走第一部分的逻辑,仔细体会这个属性名称的意思,查找已经存在的服务器,所以在bean配置时将这个属性设置为true.如下所示
<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<property name="locateExistingServerIfPossible" value="true"/>
</bean>
将此参数设置为true,此时会通过如下的方式加载MBeanServer,如果对上一章源码分析还有印象的话,应该会发现在MBeanExporter类型Bean初始化的时候也是调用的这个方法。
protected MBeanServer locateMBeanServer(@Nullable String agentId) throws MBeanServerNotFoundException {
return JmxUtils.locateMBeanServer(agentId);
}
再次启动项目通过JConsole查看。如下所示,没有问题了。
总结
本章我们通过注册Bean的方式将MBeanServer加入到Spring的管理当中,同时也发现了其实MBeanServer对象不仅仅可以有一个,还可能有多个,除了通过locateExistingServerIfPossible设置重用已经存在的MBeanServer实例之外,还可以通过agentId进行明确的区分,在MBeanExporter中通过server属性明确注册到指定的MBeanServer当中。如果未指定任何服务器,则MBeanExporter会尝试自动检测正在运行的MBeanServer。这在大多数仅使用一个MBeanServer实例的环境中都有效,但是当存在多个实例时,MBeanExporter可能会选择错误的服务器。 在这种情况下,应使用MBeanServer agentId指示要使用的实例