将您的 Bean 导出到 JMX
Spring JMX框架的核心类是 MBeanExporter。这个类负责获取你的Spring Bean并将其注册到JMX MBeanServer 中。例如,考虑以下类
-
Java
-
Kotlin
public class JmxTestBean implements IJmxTestBean {
private String name;
private int age;
@Override
public int getAge() {
return age;
}
@Override
public void setAge(int age) {
this.age = age;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int add(int x, int y) {
return x + y;
}
@Override
public void dontExposeMe() {
throw new RuntimeException();
}
}
class JmxTestBean : IJmxTestBean {
override lateinit var name: String
override var age = 0
override fun add(x: Int, y: Int): Int {
return x + y
}
override fun dontExposeMe() {
throw RuntimeException()
}
}
要将此Bean的属性和方法作为MBean的属性和操作公开,您可以在配置文件中配置 MBeanExporter 类的一个实例并传入该Bean,示例如下
-
Java
-
Kotlin
-
Xml
@Configuration
public class JmxConfiguration {
@Bean
MBeanExporter exporter(JmxTestBean testBean) {
MBeanExporter exporter = new MBeanExporter();
exporter.setBeans(Map.of("bean:name=testBean1", testBean));
return exporter;
}
@Bean
JmxTestBean testBean() {
JmxTestBean testBean = new JmxTestBean();
testBean.setName("TEST");
testBean.setAge(100);
return testBean;
}
}
@Configuration
class JmxConfiguration {
@Bean
fun exporter(testBean: JmxTestBean) = MBeanExporter().apply {
setBeans(mapOf("bean:name=testBean1" to testBean))
}
@Bean
fun testBean() = JmxTestBean().apply {
name = "TEST"
age = 100
}
}
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- this bean must not be lazily initialized if the exporting is to happen -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="bean:name=testBean1" value-ref="testBean"/>
</map>
</property>
</bean>
<bean id="testBean" class="org.example.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
上一个配置片段中的相关Bean定义是 exporter Bean。beans 属性告诉 MBeanExporter 确切地哪些Bean必须导出到JMX MBeanServer。在默认配置中,beans Map 中每个条目的键用作相应条目值引用的Bean的 ObjectName。您可以更改此行为,如 控制Bean的ObjectName实例 中所述。
通过此配置,testBean Bean在 ObjectName bean:name=testBean1 下公开为MBean。默认情况下,Bean的所有 public 属性都作为属性公开,所有 public 方法(除了从 Object 类继承的方法)都作为操作公开。
MBeanExporter 是一个 Lifecycle Bean(参见 启动和关闭回调)。默认情况下,MBeans在应用程序生命周期中尽可能晚地导出。您可以配置导出发生的 phase 或通过设置 autoStartup 标志禁用自动注册。 |
创建MBeanServer
上一节中显示的配置假设应用程序在一个已经有一个(并且只有一个)MBeanServer 运行的环境中运行。在这种情况下,Spring会尝试定位正在运行的 MBeanServer 并将您的Bean注册到该服务器(如果有)。当您的应用程序在具有自己 MBeanServer 的容器(如Tomcat或IBM WebSphere)中运行时,此行为非常有用。
然而,在独立环境或在不提供 MBeanServer 的容器中运行时,此方法毫无用处。为了解决这个问题,您可以通过在配置中添加 org.springframework.jmx.support.MBeanServerFactoryBean 类的一个实例来声明性地创建一个 MBeanServer 实例。您还可以通过将 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>
在前面的示例中,MBeanServerFactoryBean 创建了一个 MBeanServer 实例,并通过 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,并通过查找方法检索的平台或情况,您应该使用 工厂方法,示例如下
<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,并且该Bean也配置为延迟初始化,MBeanExporter 不会违反此约定,并避免实例化该Bean。相反,它会向 MBeanServer 注册一个代理,并将从容器中获取Bean的操作推迟到代理上的第一次调用发生时。
这也会影响 FactoryBean 解析,其中 MBeanExporter 会定期检查生成对象,从而有效地触发 FactoryBean.getObject()。为了避免这种情况,请将相应的Bean定义标记为延迟初始化。
MBeans的自动注册
任何通过 MBeanExporter 导出且已经是有效MBean的Bean都将原样注册到 MBeanServer,而无需Spring进一步干预。您可以通过将 autodetect 属性设置为 true 来使 MBeanExporter 自动检测MBeans,示例如下
<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:mbean=true 的Bean已经是一个有效的JMX MBean,并由Spring自动注册。默认情况下,自动检测用于JMX注册的Bean会将其Bean名称用作 ObjectName。您可以覆盖此行为,详见 控制Bean的ObjectName实例。
控制注册行为
考虑一个场景,Spring MBeanExporter 尝试使用 ObjectName bean:name=testBean1 将一个 MBean 注册到 MBeanServer。如果一个 MBean 实例已经在此相同的 ObjectName 下注册,默认行为是失败(并抛出 InstanceAlreadyExistsException)。
您可以精确控制当一个 MBean 注册到 MBeanServer 时会发生什么。Spring的JMX支持允许三种不同的注册行为来控制当注册过程发现一个 MBean 已经注册在相同的 ObjectName 下时的注册行为。下表总结了这些注册行为
| 注册行为 | 解释 |
|---|---|
|
这是默认的注册行为。如果一个 |
|
如果一个 |
|
如果一个 |
上表中定义的值是 RegistrationPolicy 类上的枚举。如果您想更改默认注册行为,您需要将 MBeanExporter 定义上的 registrationPolicy 属性值设置为其中一个值。
以下示例显示了如何从默认注册行为更改为 REPLACE_EXISTING 行为
<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>