在独立的JVM上使用Spring进行JMX MBean注册

9

根据Spring文档和互联网上的一些论坛提供的各种示例配置,我的应用程序上下文文件如下:

<beans>
    <bean id="dH" class="abc.def.ghi.DH">
        <constructor-arg>
            <value>0</value>
        </constructor-arg>
        <property name="num" value="100"/>
    </bean>
    <bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
            <property name="beans">
              <map>
                    <entry key="bean:name=dH1" value-ref="dH"/>
              </map>
            </property>
    </bean>
    <bean class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
</beans>

我正在没有任何容器和纯JVM上运行此程序。我可以通过JConsole连接到我的进程,但MBean不会显示出来。然而,通过编程方式注册bean可以成功地公开它。

MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
DH dh = new DH(0);
mbeanServer.registerMBean(dh, new ObjectName("bean:name=dH1"));

我尝试了调整Spring配置,但没有成功。我认为该bean没有注册到已经在ManagementFactory.getPlatformMBeanServer()中可访问的运行中MBean服务器上。对此有什么想法吗?

2个回答

12

除了使用...,还可以定义一个MBeanServerFactory bean(如Nicholas在他们的答案中所指出的)...

<bean class="org.springframework.jmx.support.MBeanServerFactoryBean">
    <property name="locateExistingServerIfPossible" value="true" />
</bean>

...你需要告诉MBeanExporter要管理什么:

如果一个bean实现了JMX管理接口之一,MBeanExporter可以通过其自动检测过程将MBean注册到服务器上。

如果一个bean没有实现JMX管理接口之一,则MBeanExporter将使用提供的MBeanInfoAssembler创建管理信息。

假设你的abc.def.ghi.DH类没有实现任何JMX接口,请尝试将MBeanExporter定义为:

<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
    <property name="assembler">
        <bean
            class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler"
        >
            <property name="managedMethods">
                <list>
                    <value>getNum</value>
                </list>
            </property>
        </bean>
    </property>
    <property name="beans">
        <map>
            <entry key="bean:name=dH1" value-ref="dH"/>
        </map>
    </property>
</bean>

观察OpenJDK 7,更新2,构建21的源代码DefaultMBeanServerInterceptor.java,第898行创建了一个常规对象的DynamicMBean。请注意保留HTML标记。
DynamicMBean mbean = Introspector.makeDynamicMBean(object);

我还没有调试过,但我敢打赌 mbeanServer.registerMBean(dh, new ObjectName("bean:name=dH1")) 最终会调用 DefaultMBeanServerInterceptor.registerObject(),它会为您创建一个 DynamicMBean 并正确注册您的标准 JavaBean 属性的设置器和获取器。


以下是一些使用 Spring Framework 3.0.5 和 Oracle HotSpot Java 1.6.0_24 工作的测试文件。在设置了您的 CLASSPATH 环境变量后,只需运行 javac *.javajava Main,并使用 VisualVM(或类似应用程序)连接到正在运行的 Java 应用程序,以查看已注册的 MBeans。

ac.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd"
    default-lazy-init="true"
>
    <bean id="test" class="Test" />
    <bean class="org.springframework.jmx.support.MBeanServerFactoryBean">
        <property name="locateExistingServerIfPossible" value="true" />
    </bean>
    <bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
        <property name="assembler">
            <bean
                class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler"
            >
                <property name="managedMethods">
                    <list>
                        <value>getVal</value>
                        <value>setVal</value>
                    </list>
                </property>
            </bean>
        </property>
        <property name="beans">
            <map>
                <entry key="bean:name=Test" value-ref="test"/>
            </map>
        </property>
    </bean>
</beans>

Test.java:

public class Test {
    private String val = "";
    public String getVal() {
        return val;
    }
    public void setVal(String v) {
        val = v;
    }
}

Main.java:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
    public static void main(final String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("ac.xml");
        try {
            Thread.sleep(1000 * 60 * 5);
        } catch (final Throwable t) {}
    }
}

感谢您挖掘这个问题 @Dan。当Spring上下文被加载或者使用上下文创建了dh时,Introspector行甚至不会被执行。但是手动注册bean确实会调用相同的行。我不认为Spring正在尝试使用这些配置注册bean。肯定是有遗漏的…… - Monis Iqbal
这对我来说可行,使用Spring 3.0.5和一个具有一个属性getter/setter的简单JavaBean;我可以使用VisualVM连接到我的简单测试应用程序,并查看使用我提供的“MBeanExporter”bean定义注册的测试MBean。尝试将您的场景分解为简单形式,使其正常工作,然后将其构建回到您当前拥有的内容,并查看您的问题所在。 - Go Dan
使用Spring 3.1.0,我尝试使用您提供的XML文件,并尝试了-Dcom.sun.management.jmxremote作为程序选项,但仍然没有成功。 我是按照以下方式加载上下文的: BeanFactory factory = new XmlBeanFactory(new ClassPathResource("/application-context.xml")); 然后将预期的MBean加载为: factory.getBean(DH.class); 我知道这听起来很幼稚,但我们是否有办法匹配完整的XML和简单的程序结构? - Monis Iqbal
非常感谢 @Dan。问题出在 bean 的初始化方式上 :|。我之前使用的是 BeanFactory,而改用 ApplicationContext 就解决了问题。 - Monis Iqbal

2
问题出在MBeanServerFactoryBean上。
javadoc中可以看到:
默认情况下,即使已经有一个MBeanServer正在运行,MBeanServerFactoryBean仍将创建一个新的MBeanServer。要让MBeanServerFactoryBean首先尝试定位正在运行的MBeanServer,请将“locateExistingServerIfPossible”属性的值设置为“true”。
请尝试使用此配置:
<bean class="org.springframework.jmx.support.MBeanServerFactoryBean">
    <property name="locateExistingServerIfPossible" value="true" />
</bean>

=================================================

尝试在导出器bean中指定MBeanServer:
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
    <property name="beans">
        <map>
                <entry key="bean:name=dH1" value-ref="dH" />
            </map>
        </property>
        <property name="server" ref="MBeanServer" />
</bean>
<bean id="MBeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
    <property name="locateExistingServerIfPossible" value="true" />
</bean>

========================================================================

好的,让我们采用蛮力方法直接获取平台MBeanServer:
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
    <property name="beans">
        <map>
                <entry key="bean:name=dH1" value-ref="dH" />
            </map>
        </property>
        <property name="server">
            <bean id="MBeanServer" class="java.lang.management.ManagementFactory" factory-method="getPlatformMBeanServer"/>
        </property>
</bean>

感谢@Nicholas提供的其他建议。尝试了两种方法,但都没有成功注册MBeans。 - Monis Iqbal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接